mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 09:53:18 +00:00
Merge branch 'openjdk:master' into cas-alloc-1
This commit is contained in:
commit
9e0520ba48
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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
|
||||
@ -6086,7 +6086,7 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned,
|
||||
vpbroadcastd(xtmp, xtmp, Assembler::AVX_512bit);
|
||||
|
||||
subptr(count, 16 << shift);
|
||||
jccb(Assembler::less, L_check_fill_32_bytes);
|
||||
jcc(Assembler::less, L_check_fill_32_bytes);
|
||||
align(16);
|
||||
|
||||
BIND(L_fill_64_bytes_loop_avx3);
|
||||
|
||||
@ -458,12 +458,10 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) {
|
||||
warning("Failed mmap to file. (%s)", os::strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
if (base != nullptr && addr != base) {
|
||||
if (!os::release_memory(addr, size)) {
|
||||
warning("Could not release memory on unsuccessful file mapping");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The requested address should be the same as the returned address when using MAP_FIXED
|
||||
// as per POSIX.
|
||||
assert(base == nullptr || addr == base, "base should equal addr when using MAP_FIXED");
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2021 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026 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
|
||||
@ -946,7 +946,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size
|
||||
if (result == -1 ) break;
|
||||
if (!os::write(fd, &zero_int, 1)) {
|
||||
if (errno == ENOSPC) {
|
||||
warning("Insufficient space for shared memory file:\n %s\nTry using the -Djava.io.tmpdir= option to select an alternate temp location.\n", filename);
|
||||
warning("Insufficient space for shared memory file: %s/%s\n", dirname, filename);
|
||||
}
|
||||
result = OS_ERR;
|
||||
break;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2026, 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
|
||||
@ -951,6 +951,32 @@ struct enum_sigcode_desc_t {
|
||||
const char* s_desc;
|
||||
};
|
||||
|
||||
#if defined(LINUX)
|
||||
// Additional kernel si_code definitions that are only exported by
|
||||
// more recent glibc distributions, so we have to hard-code the values.
|
||||
#ifndef BUS_MCEERR_AR // glibc 2.17
|
||||
#define BUS_MCEERR_AR 4
|
||||
#define BUS_MCEERR_AO 5
|
||||
#endif
|
||||
|
||||
#ifndef SEGV_PKUERR // glibc 2.27
|
||||
#define SEGV_PKUERR 4
|
||||
#endif
|
||||
|
||||
#ifndef SYS_SECCOMP // glibc 2.28
|
||||
#define SYS_SECCOMP 1
|
||||
#endif
|
||||
|
||||
#ifndef TRAP_BRANCH // glibc 2.30
|
||||
#define TRAP_BRANCH 3
|
||||
#endif
|
||||
|
||||
#ifndef TRAP_HWBKPT // not glibc version specific - gdb related
|
||||
#define TRAP_HWBKPT 4
|
||||
#endif
|
||||
|
||||
#endif // LINUX
|
||||
|
||||
static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t* out) {
|
||||
|
||||
const struct {
|
||||
@ -976,6 +1002,7 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t
|
||||
{ SIGSEGV, SEGV_ACCERR, "SEGV_ACCERR", "Invalid permissions for mapped object." },
|
||||
#if defined(LINUX)
|
||||
{ SIGSEGV, SEGV_BNDERR, "SEGV_BNDERR", "Failed address bound checks." },
|
||||
{ SIGSEGV, SEGV_PKUERR, "SEGV_PKUERR", "Protection key checking failure." },
|
||||
#endif
|
||||
#if defined(AIX)
|
||||
// no explanation found what keyerr would be
|
||||
@ -984,8 +1011,18 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t
|
||||
{ SIGBUS, BUS_ADRALN, "BUS_ADRALN", "Invalid address alignment." },
|
||||
{ SIGBUS, BUS_ADRERR, "BUS_ADRERR", "Nonexistent physical address." },
|
||||
{ SIGBUS, BUS_OBJERR, "BUS_OBJERR", "Object-specific hardware error." },
|
||||
#if defined(LINUX)
|
||||
{ SIGBUS, BUS_MCEERR_AR,"BUS_MCEERR_AR","Hardware memory error consumed on a machine check: action required." },
|
||||
{ SIGBUS, BUS_MCEERR_AO,"BUS_MCEERR_AO","Hardware memory error detected in process but not consumed: action optional." },
|
||||
|
||||
{ SIGSYS, SYS_SECCOMP, "SYS_SECCOMP", "Secure computing (seccomp) filter failure." },
|
||||
#endif
|
||||
{ SIGTRAP, TRAP_BRKPT, "TRAP_BRKPT", "Process breakpoint." },
|
||||
{ SIGTRAP, TRAP_TRACE, "TRAP_TRACE", "Process trace trap." },
|
||||
#if defined(LINUX)
|
||||
{ SIGTRAP, TRAP_BRANCH, "TRAP_BRANCH", "Process taken branch trap." },
|
||||
{ SIGTRAP, TRAP_HWBKPT, "TRAP_HWBKPT", "Hardware breakpoint/watchpoint." },
|
||||
#endif
|
||||
{ SIGCHLD, CLD_EXITED, "CLD_EXITED", "Child has exited." },
|
||||
{ SIGCHLD, CLD_KILLED, "CLD_KILLED", "Child has terminated abnormally and did not create a core file." },
|
||||
{ SIGCHLD, CLD_DUMPED, "CLD_DUMPED", "Child has terminated abnormally and created a core file." },
|
||||
@ -993,6 +1030,7 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t
|
||||
{ SIGCHLD, CLD_STOPPED, "CLD_STOPPED", "Child has stopped." },
|
||||
{ SIGCHLD, CLD_CONTINUED,"CLD_CONTINUED","Stopped child has continued." },
|
||||
#ifdef SIGPOLL
|
||||
{ SIGPOLL, POLL_IN, "POLL_IN", "Data input available." },
|
||||
{ SIGPOLL, POLL_OUT, "POLL_OUT", "Output buffers available." },
|
||||
{ SIGPOLL, POLL_MSG, "POLL_MSG", "Input message available." },
|
||||
{ SIGPOLL, POLL_ERR, "POLL_ERR", "I/O error." },
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2013 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,8 +26,7 @@
|
||||
#ifndef OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP
|
||||
#define OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read(const void *loc, intx interval) {
|
||||
#if !defined(USE_XLC_BUILTINS)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
|
||||
* Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
@ -27,8 +27,7 @@
|
||||
#ifndef OS_CPU_BSD_AARCH64_PREFETCH_BSD_AARCH64_INLINE_HPP
|
||||
#define OS_CPU_BSD_AARCH64_PREFETCH_BSD_AARCH64_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
if (interval >= 0)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, 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,7 @@
|
||||
#ifndef OS_CPU_BSD_X86_PREFETCH_BSD_X86_INLINE_HPP
|
||||
#define OS_CPU_BSD_X86_PREFETCH_BSD_X86_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
__asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2007, 2008 Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,7 +26,7 @@
|
||||
#ifndef OS_CPU_BSD_ZERO_PREFETCH_BSD_ZERO_INLINE_HPP
|
||||
#define OS_CPU_BSD_ZERO_PREFETCH_BSD_ZERO_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read(const void* loc, intx interval) {
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,8 +26,7 @@
|
||||
#ifndef OS_CPU_LINUX_AARCH64_PREFETCH_LINUX_AARCH64_INLINE_HPP
|
||||
#define OS_CPU_LINUX_AARCH64_PREFETCH_LINUX_AARCH64_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
if (interval >= 0)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2026, 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 @@
|
||||
#ifndef OS_CPU_LINUX_ARM_PREFETCH_LINUX_ARM_INLINE_HPP
|
||||
#define OS_CPU_LINUX_ARM_PREFETCH_LINUX_ARM_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_5TE__)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2013 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,8 +26,7 @@
|
||||
#ifndef OS_CPU_LINUX_PPC_PREFETCH_LINUX_PPC_INLINE_HPP
|
||||
#define OS_CPU_LINUX_PPC_PREFETCH_LINUX_PPC_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read(const void *loc, intx interval) {
|
||||
__asm__ __volatile__ (
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,7 +26,7 @@
|
||||
#ifndef OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP
|
||||
#define OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
if (interval >= 0 && UseZicbop) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,7 +26,7 @@
|
||||
#ifndef OS_CPU_LINUX_S390_PREFETCH_LINUX_S390_INLINE_HPP
|
||||
#define OS_CPU_LINUX_S390_PREFETCH_LINUX_S390_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read(const void* loc, intx interval) {
|
||||
// No prefetch instructions on z/Architecture -> implement trivially.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, 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,7 @@
|
||||
#ifndef OS_CPU_LINUX_X86_PREFETCH_LINUX_X86_INLINE_HPP
|
||||
#define OS_CPU_LINUX_X86_PREFETCH_LINUX_X86_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
__asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2007, 2008 Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -26,7 +26,7 @@
|
||||
#ifndef OS_CPU_LINUX_ZERO_PREFETCH_LINUX_ZERO_INLINE_HPP
|
||||
#define OS_CPU_LINUX_ZERO_PREFETCH_LINUX_ZERO_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read(const void* loc, intx interval) {
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Microsoft Corporation. All rights reserved.
|
||||
* Copyright (c) 2020, 2026, Microsoft 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
|
||||
@ -25,8 +25,7 @@
|
||||
#ifndef OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP
|
||||
#define OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, 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 @@
|
||||
#ifndef OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP
|
||||
#define OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
// Included in runtime/prefetch.inline.hpp
|
||||
|
||||
inline void Prefetch::read (const void *loc, intx interval) {}
|
||||
inline void Prefetch::write(void *loc, intx interval) {}
|
||||
|
||||
@ -62,8 +62,6 @@ jint EpsilonHeap::initialize() {
|
||||
|
||||
// Enable monitoring
|
||||
_monitoring_support = new EpsilonMonitoringSupport(this);
|
||||
_last_counter_update = 0;
|
||||
_last_heap_print = 0;
|
||||
|
||||
// Install barrier set
|
||||
BarrierSet::set_barrier_set(new EpsilonBarrierSet());
|
||||
@ -77,6 +75,7 @@ jint EpsilonHeap::initialize() {
|
||||
void EpsilonHeap::initialize_serviceability() {
|
||||
_pool = new EpsilonMemoryPool(this);
|
||||
_memory_manager.add_pool(_pool);
|
||||
_monitoring_support->mark_ready();
|
||||
}
|
||||
|
||||
GrowableArray<GCMemoryManager*> EpsilonHeap::memory_managers() {
|
||||
@ -101,7 +100,7 @@ EpsilonHeap* EpsilonHeap::heap() {
|
||||
return named_heap<EpsilonHeap>(CollectedHeap::Epsilon);
|
||||
}
|
||||
|
||||
HeapWord* EpsilonHeap::allocate_work(size_t size, bool verbose) {
|
||||
HeapWord* EpsilonHeap::allocate_work(size_t size) {
|
||||
assert(is_object_aligned(size), "Allocation size should be aligned: %zu", size);
|
||||
|
||||
HeapWord* res = nullptr;
|
||||
@ -151,19 +150,23 @@ HeapWord* EpsilonHeap::allocate_work(size_t size, bool verbose) {
|
||||
|
||||
size_t used = _space->used();
|
||||
|
||||
// Allocation successful, update counters
|
||||
if (verbose) {
|
||||
size_t last = _last_counter_update;
|
||||
if ((used - last >= _step_counter_update) && AtomicAccess::cmpxchg(&_last_counter_update, last, used) == last) {
|
||||
// Allocation successful, update counters and print status.
|
||||
// At this point, some diagnostic subsystems might not yet be initialized.
|
||||
// We pretend the printout happened either way. This keeps allocation path
|
||||
// from obsessively checking the subsystems' status on every allocation.
|
||||
size_t last_counter = _last_counter_update.load_relaxed();
|
||||
if ((used - last_counter >= _step_counter_update) &&
|
||||
_last_counter_update.compare_set(last_counter, used)) {
|
||||
if (_monitoring_support->is_ready()) {
|
||||
_monitoring_support->update_counters();
|
||||
}
|
||||
}
|
||||
|
||||
// ...and print the occupancy line, if needed
|
||||
if (verbose) {
|
||||
size_t last = _last_heap_print;
|
||||
if ((used - last >= _step_heap_print) && AtomicAccess::cmpxchg(&_last_heap_print, last, used) == last) {
|
||||
print_heap_info(used);
|
||||
size_t last_heap = _last_heap_print.load_relaxed();
|
||||
if ((used - last_heap >= _step_heap_print) &&
|
||||
_last_heap_print.compare_set(last_heap, used)) {
|
||||
print_heap_info(used);
|
||||
if (Metaspace::initialized()) {
|
||||
print_metaspace_info();
|
||||
}
|
||||
}
|
||||
@ -265,8 +268,7 @@ HeapWord* EpsilonHeap::mem_allocate(size_t size) {
|
||||
}
|
||||
|
||||
HeapWord* EpsilonHeap::allocate_loaded_archive_space(size_t size) {
|
||||
// Cannot use verbose=true because Metaspace is not initialized
|
||||
return allocate_work(size, /* verbose = */false);
|
||||
return allocate_work(size);
|
||||
}
|
||||
|
||||
void EpsilonHeap::collect(GCCause::Cause cause) {
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include "gc/shared/collectedHeap.hpp"
|
||||
#include "gc/shared/space.hpp"
|
||||
#include "memory/virtualspace.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "services/memoryManager.hpp"
|
||||
|
||||
class EpsilonHeap : public CollectedHeap {
|
||||
@ -45,8 +46,8 @@ private:
|
||||
size_t _step_counter_update;
|
||||
size_t _step_heap_print;
|
||||
int64_t _decay_time_ns;
|
||||
volatile size_t _last_counter_update;
|
||||
volatile size_t _last_heap_print;
|
||||
Atomic<size_t> _last_counter_update;
|
||||
Atomic<size_t> _last_heap_print;
|
||||
|
||||
void print_tracing_info() const override;
|
||||
void stop() override {};
|
||||
@ -83,7 +84,7 @@ public:
|
||||
bool requires_barriers(stackChunkOop obj) const override { return false; }
|
||||
|
||||
// Allocation
|
||||
HeapWord* allocate_work(size_t size, bool verbose = true);
|
||||
HeapWord* allocate_work(size_t size);
|
||||
HeapWord* mem_allocate(size_t size) override;
|
||||
HeapWord* allocate_new_tlab(size_t min_size,
|
||||
size_t requested_size,
|
||||
|
||||
@ -99,6 +99,7 @@ EpsilonMonitoringSupport::EpsilonMonitoringSupport(EpsilonHeap* heap) {
|
||||
}
|
||||
|
||||
void EpsilonMonitoringSupport::update_counters() {
|
||||
assert(is_ready(), "Must be ready");
|
||||
MemoryService::track_memory_usage();
|
||||
|
||||
if (UsePerfData) {
|
||||
@ -110,3 +111,11 @@ void EpsilonMonitoringSupport::update_counters() {
|
||||
MetaspaceCounters::update_performance_counters();
|
||||
}
|
||||
}
|
||||
|
||||
bool EpsilonMonitoringSupport::is_ready() {
|
||||
return _ready.load_acquire();
|
||||
}
|
||||
|
||||
void EpsilonMonitoringSupport::mark_ready() {
|
||||
_ready.release_store(true);
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#define SHARE_GC_EPSILON_EPSILONMONITORINGSUPPORT_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
|
||||
class EpsilonGenerationCounters;
|
||||
class EpsilonSpaceCounters;
|
||||
@ -35,9 +36,12 @@ class EpsilonMonitoringSupport : public CHeapObj<mtGC> {
|
||||
private:
|
||||
EpsilonGenerationCounters* _heap_counters;
|
||||
EpsilonSpaceCounters* _space_counters;
|
||||
Atomic<bool> _ready;
|
||||
|
||||
public:
|
||||
EpsilonMonitoringSupport(EpsilonHeap* heap);
|
||||
bool is_ready();
|
||||
void mark_ready();
|
||||
void update_counters();
|
||||
};
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
#include "gc/shared/fullGCForwarding.hpp"
|
||||
#include "gc/shared/gcArguments.hpp"
|
||||
#include "gc/shared/workerPolicy.hpp"
|
||||
#include "runtime/flags/jvmFlagLimit.hpp"
|
||||
#include "runtime/globals.hpp"
|
||||
#include "runtime/globals_extension.hpp"
|
||||
#include "runtime/java.hpp"
|
||||
@ -190,7 +191,8 @@ void G1Arguments::initialize() {
|
||||
}
|
||||
FLAG_SET_DEFAULT(G1ConcRefinementThreads, 0);
|
||||
} else if (FLAG_IS_DEFAULT(G1ConcRefinementThreads)) {
|
||||
FLAG_SET_ERGO(G1ConcRefinementThreads, ParallelGCThreads);
|
||||
const JVMTypedFlagLimit<uint>* conc_refinement_threads_limits = JVMFlagLimit::get_range_at(FLAG_MEMBER_ENUM(G1ConcRefinementThreads))->cast<uint>();
|
||||
FLAG_SET_ERGO(G1ConcRefinementThreads, MIN2(ParallelGCThreads, conc_refinement_threads_limits->max()));
|
||||
}
|
||||
|
||||
if (FLAG_IS_DEFAULT(ConcGCThreads) || ConcGCThreads == 0) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2026, 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
|
||||
@ -686,7 +686,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {
|
||||
// the check before we do the actual allocation. The reason for doing it
|
||||
// before the allocation is that we avoid having to keep track of the newly
|
||||
// allocated memory while we do a GC.
|
||||
if (policy()->need_to_start_conc_mark("concurrent humongous allocation",
|
||||
// Only try that if we can actually perform a GC.
|
||||
if (is_init_completed() && policy()->need_to_start_conc_mark("concurrent humongous allocation",
|
||||
word_size)) {
|
||||
try_collect(word_size, GCCause::_g1_humongous_allocation, collection_counters(this));
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2026, 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,7 +46,7 @@
|
||||
#include "oops/access.inline.hpp"
|
||||
#include "oops/compressedOops.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/prefetch.hpp"
|
||||
#include "runtime/prefetch.inline.hpp"
|
||||
#include "runtime/threads.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "utilities/bitMap.inline.hpp"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2026, 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,6 +28,7 @@
|
||||
#include "gc/serial/serialHeap.inline.hpp"
|
||||
#include "gc/shared/space.hpp"
|
||||
#include "memory/iterator.inline.hpp"
|
||||
#include "runtime/prefetch.inline.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
|
||||
void CardTableRS::scan_old_to_young_refs(TenuredGeneration* tg, HeapWord* saved_top) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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
|
||||
@ -34,7 +34,6 @@
|
||||
#include "memory/virtualspace.hpp"
|
||||
#include "runtime/mutex.hpp"
|
||||
#include "runtime/perfData.hpp"
|
||||
#include "runtime/prefetch.inline.hpp"
|
||||
|
||||
// A Generation models a heap area for similarly-aged objects.
|
||||
// It will contain one ore more spaces holding the actual objects.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2026, 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
|
||||
@ -70,6 +70,7 @@
|
||||
#include "runtime/init.hpp"
|
||||
#include "runtime/java.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/prefetch.inline.hpp"
|
||||
#include "runtime/threads.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "services/memoryManager.hpp"
|
||||
|
||||
@ -700,7 +700,7 @@ void OopStorage::Block::release_entries(uintx releasing, OopStorage* owner) {
|
||||
// then someone else has made such a claim and the deferred update has not
|
||||
// yet been processed and will include our change, so we don't need to do
|
||||
// anything further.
|
||||
if (_deferred_updates_next.compare_exchange(nullptr, this) == nullptr) {
|
||||
if (_deferred_updates_next.compare_set(nullptr, this)) {
|
||||
// Successfully claimed. Push, with self-loop for end-of-list.
|
||||
Block* head = owner->_deferred_updates.load_relaxed();
|
||||
while (true) {
|
||||
|
||||
@ -56,7 +56,7 @@ void PretouchTask::work(uint worker_id) {
|
||||
char* cur_end = cur_start + MIN2(_chunk_size, pointer_delta(_end_addr, cur_start, 1));
|
||||
if (cur_start >= cur_end) {
|
||||
break;
|
||||
} else if (cur_start == _cur_addr.compare_exchange(cur_start, cur_end)) {
|
||||
} else if (_cur_addr.compare_set(cur_start, cur_end)) {
|
||||
os::pretouch_memory(cur_start, cur_end, _page_size);
|
||||
} // Else attempt to claim chunk failed, so try again.
|
||||
}
|
||||
|
||||
@ -183,8 +183,8 @@ protected:
|
||||
_age.store_relaxed(new_age);
|
||||
}
|
||||
|
||||
Age cmpxchg_age(Age old_age, Age new_age) {
|
||||
return _age.compare_exchange(old_age, new_age);
|
||||
bool par_set_age(Age old_age, Age new_age) {
|
||||
return _age.compare_set(old_age, new_age);
|
||||
}
|
||||
|
||||
idx_t age_top_relaxed() const {
|
||||
@ -345,7 +345,7 @@ protected:
|
||||
|
||||
using TaskQueueSuper<N, MT>::age_relaxed;
|
||||
using TaskQueueSuper<N, MT>::set_age_relaxed;
|
||||
using TaskQueueSuper<N, MT>::cmpxchg_age;
|
||||
using TaskQueueSuper<N, MT>::par_set_age;
|
||||
using TaskQueueSuper<N, MT>::age_top_relaxed;
|
||||
|
||||
using TaskQueueSuper<N, MT>::increment_index;
|
||||
|
||||
@ -170,8 +170,7 @@ bool GenericTaskQueue<E, MT, N>::pop_local_slow(uint localBot, Age oldAge) {
|
||||
if (localBot == oldAge.top()) {
|
||||
// No competing pop_global has yet incremented "top"; we'll try to
|
||||
// install new_age, thus claiming the element.
|
||||
Age tempAge = cmpxchg_age(oldAge, newAge);
|
||||
if (tempAge == oldAge) {
|
||||
if (par_set_age(oldAge, newAge)) {
|
||||
// We win.
|
||||
assert_not_underflow(localBot, age_top_relaxed());
|
||||
TASKQUEUE_STATS_ONLY(stats.record_pop_slow());
|
||||
@ -283,12 +282,12 @@ typename GenericTaskQueue<E, MT, N>::PopResult GenericTaskQueue<E, MT, N>::pop_g
|
||||
idx_t new_top = increment_index(oldAge.top());
|
||||
idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0);
|
||||
Age newAge(new_top, new_tag);
|
||||
Age resAge = cmpxchg_age(oldAge, newAge);
|
||||
bool result = par_set_age(oldAge, newAge);
|
||||
|
||||
// Note that using "bottom" here might fail, since a pop_local might
|
||||
// have decremented it.
|
||||
assert_not_underflow(localBot, newAge.top());
|
||||
return resAge == oldAge ? PopResult::Success : PopResult::Contended;
|
||||
return result ? PopResult::Success : PopResult::Contended;
|
||||
}
|
||||
|
||||
inline int randomParkAndMiller(int *seed0) {
|
||||
|
||||
@ -61,7 +61,12 @@ ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() :
|
||||
|
||||
void ShenandoahGenerationalControlThread::run_service() {
|
||||
|
||||
// This is the only instance of request. It is important that request.generation
|
||||
// does not change between a concurrent cycle failure and the start of a degenerated
|
||||
// cycle. We initialize it with the young generation to handle the pathological case
|
||||
// where the very first cycle is degenerated (some tests exercise this path).
|
||||
ShenandoahGCRequest request;
|
||||
request.generation = _heap->young_generation();
|
||||
while (!should_terminate()) {
|
||||
|
||||
// Figure out if we have pending requests.
|
||||
@ -77,12 +82,10 @@ void ShenandoahGenerationalControlThread::run_service() {
|
||||
|
||||
// If the cycle was cancelled, continue the next iteration to deal with it. Otherwise,
|
||||
// if there was no other cycle requested, cleanup and wait for the next request.
|
||||
if (!_heap->cancelled_gc()) {
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (_requested_gc_cause == GCCause::_no_gc) {
|
||||
set_gc_mode(ml, none);
|
||||
ml.wait();
|
||||
}
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (_requested_gc_cause == GCCause::_no_gc) {
|
||||
set_gc_mode(ml, none);
|
||||
ml.wait();
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,8 +99,7 @@ void ShenandoahGenerationalControlThread::stop_service() {
|
||||
log_debug(gc, thread)("Stopping control thread");
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
_heap->cancel_gc(GCCause::_shenandoah_stop_vm);
|
||||
_requested_gc_cause = GCCause::_shenandoah_stop_vm;
|
||||
notify_cancellation(ml, GCCause::_shenandoah_stop_vm);
|
||||
notify_control_thread(ml, GCCause::_shenandoah_stop_vm);
|
||||
// We can't wait here because it may interfere with the active cycle's ability
|
||||
// to reach a safepoint (this runs on a java thread).
|
||||
}
|
||||
@ -105,29 +107,39 @@ void ShenandoahGenerationalControlThread::stop_service() {
|
||||
void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& request) {
|
||||
// Hold the lock while we read request cause and generation
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (_heap->cancelled_gc()) {
|
||||
// The previous request was cancelled. Either it was cancelled for an allocation
|
||||
// failure (degenerated cycle), or old marking was cancelled to run a young collection.
|
||||
// In either case, the correct generation for the next cycle can be determined by
|
||||
// the cancellation cause.
|
||||
request.cause = _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc);
|
||||
if (request.cause == GCCause::_shenandoah_concurrent_gc) {
|
||||
|
||||
log_debug(gc, thread)("cancelled cause: %s, requested cause: %s",
|
||||
GCCause::to_string(_heap->cancelled_cause()), GCCause::to_string(_requested_gc_cause));
|
||||
|
||||
request.cause = _requested_gc_cause;
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(request.cause)) {
|
||||
if (_degen_point == ShenandoahGC::_degenerated_unset) {
|
||||
request.generation = _heap->young_generation();
|
||||
_degen_point = ShenandoahGC::_degenerated_outside_cycle;
|
||||
} else {
|
||||
assert(request.generation != nullptr, "Must know which generation to use for degenerated cycle");
|
||||
}
|
||||
} else {
|
||||
request.cause = _requested_gc_cause;
|
||||
if (request.cause == GCCause::_shenandoah_concurrent_gc) {
|
||||
// This is a regulator request. It is also possible that the regulator "canceled" an old mark,
|
||||
// so we can clear that here. This clear operation will only clear the cancellation if it is
|
||||
// a regulator request.
|
||||
_heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc);
|
||||
}
|
||||
request.generation = _requested_generation;
|
||||
|
||||
// Only clear these if we made a request from them. In the case of a cancelled gc,
|
||||
// we do not want to inadvertently lose this pending request.
|
||||
_requested_gc_cause = GCCause::_no_gc;
|
||||
_requested_generation = nullptr;
|
||||
}
|
||||
|
||||
log_debug(gc, thread)("request.cause: %s, request.generation: %s",
|
||||
GCCause::to_string(request.cause), request.generation == nullptr ? "None" : request.generation->name());
|
||||
|
||||
_requested_gc_cause = GCCause::_no_gc;
|
||||
_requested_generation = nullptr;
|
||||
|
||||
if (request.cause == GCCause::_no_gc || request.cause == GCCause::_shenandoah_stop_vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(request.generation != nullptr, "request.generation cannot be null, cause is: %s", GCCause::to_string(request.cause));
|
||||
GCMode mode;
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(request.cause)) {
|
||||
mode = prepare_for_allocation_failure_gc(request);
|
||||
@ -140,11 +152,9 @@ void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest&
|
||||
}
|
||||
|
||||
ShenandoahGenerationalControlThread::GCMode ShenandoahGenerationalControlThread::prepare_for_allocation_failure_gc(ShenandoahGCRequest &request) {
|
||||
|
||||
if (_degen_point == ShenandoahGC::_degenerated_unset) {
|
||||
_degen_point = ShenandoahGC::_degenerated_outside_cycle;
|
||||
request.generation = _heap->young_generation();
|
||||
} else if (request.generation->is_old()) {
|
||||
// Important: not all paths update the request.generation. This is intentional.
|
||||
// A degenerated cycle must use the same generation carried over from the previous request.
|
||||
if (request.generation->is_old()) {
|
||||
// This means we degenerated during the young bootstrap for the old generation
|
||||
// cycle. The following degenerated cycle should therefore also be young.
|
||||
request.generation = _heap->young_generation();
|
||||
@ -588,6 +598,8 @@ bool ShenandoahGenerationalControlThread::check_cancellation_or_degen(Shenandoah
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(_heap->cancelled_cause())) {
|
||||
assert(_degen_point == ShenandoahGC::_degenerated_unset,
|
||||
"Should not be set yet: %s", ShenandoahGC::degen_point_to_string(_degen_point));
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
_requested_gc_cause = _heap->cancelled_cause();
|
||||
_degen_point = point;
|
||||
log_debug(gc, thread)("Cancellation detected:, reason: %s, degen point: %s",
|
||||
GCCause::to_string(_heap->cancelled_cause()),
|
||||
@ -633,9 +645,7 @@ void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const Sh
|
||||
|
||||
void ShenandoahGenerationalControlThread::request_gc(GCCause::Cause cause) {
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(cause)) {
|
||||
// GC should already be cancelled. Here we are just notifying the control thread to
|
||||
// wake up and handle the cancellation request, so we don't need to set _requested_gc_cause.
|
||||
notify_cancellation(cause);
|
||||
notify_control_thread(cause);
|
||||
} else if (ShenandoahCollectorPolicy::should_handle_requested_gc(cause)) {
|
||||
handle_requested_gc(cause);
|
||||
}
|
||||
@ -653,7 +663,8 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (gc_mode() == servicing_old) {
|
||||
if (!preempt_old_marking(generation)) {
|
||||
log_debug(gc, thread)("Cannot start young, old collection is not preemptible");
|
||||
// Global should be able to cause old collection to be abandoned
|
||||
log_debug(gc, thread)("Cannot start %s, old collection is not preemptible", generation->name());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -661,7 +672,7 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera
|
||||
log_info(gc)("Preempting old generation mark to allow %s GC", generation->name());
|
||||
while (gc_mode() == servicing_old) {
|
||||
ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_concurrent_gc);
|
||||
notify_cancellation(ml, GCCause::_shenandoah_concurrent_gc);
|
||||
notify_control_thread(ml, GCCause::_shenandoah_concurrent_gc, generation);
|
||||
ml.wait();
|
||||
}
|
||||
return true;
|
||||
@ -695,21 +706,34 @@ void ShenandoahGenerationalControlThread::notify_control_thread(GCCause::Cause c
|
||||
|
||||
void ShenandoahGenerationalControlThread::notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation) {
|
||||
assert(_control_lock.is_locked(), "Request lock must be held here");
|
||||
log_debug(gc, thread)("Notify control (%s): %s, %s", gc_mode_name(gc_mode()), GCCause::to_string(cause), generation->name());
|
||||
_requested_gc_cause = cause;
|
||||
_requested_generation = generation;
|
||||
ml.notify();
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(_requested_gc_cause)) {
|
||||
// We have already observed a request to handle an allocation failure. We cannot allow
|
||||
// another request (System.gc or regulator) to subvert the degenerated cycle.
|
||||
log_debug(gc, thread)("Not overwriting gc cause %s with %s", GCCause::to_string(_requested_gc_cause), GCCause::to_string(cause));
|
||||
} else {
|
||||
log_debug(gc, thread)("Notify control (%s): %s, %s", gc_mode_name(gc_mode()), GCCause::to_string(cause), generation->name());
|
||||
_requested_gc_cause = cause;
|
||||
_requested_generation = generation;
|
||||
ml.notify();
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahGenerationalControlThread::notify_cancellation(GCCause::Cause cause) {
|
||||
void ShenandoahGenerationalControlThread::notify_control_thread(GCCause::Cause cause) {
|
||||
MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag);
|
||||
notify_cancellation(ml, cause);
|
||||
notify_control_thread(ml, cause);
|
||||
}
|
||||
|
||||
void ShenandoahGenerationalControlThread::notify_cancellation(MonitorLocker& ml, GCCause::Cause cause) {
|
||||
assert(_heap->cancelled_gc(), "GC should already be cancelled");
|
||||
log_debug(gc,thread)("Notify control (%s): %s", gc_mode_name(gc_mode()), GCCause::to_string(cause));
|
||||
ml.notify();
|
||||
void ShenandoahGenerationalControlThread::notify_control_thread(MonitorLocker& ml, GCCause::Cause cause) {
|
||||
assert(_control_lock.is_locked(), "Request lock must be held here");
|
||||
if (ShenandoahCollectorPolicy::is_allocation_failure(_requested_gc_cause)) {
|
||||
// We have already observed a request to handle an allocation failure. We cannot allow
|
||||
// another request (System.gc or regulator) to subvert the degenerated cycle.
|
||||
log_debug(gc, thread)("Not overwriting gc cause %s with %s", GCCause::to_string(_requested_gc_cause), GCCause::to_string(cause));
|
||||
} else {
|
||||
log_debug(gc, thread)("Notify control (%s): %s", gc_mode_name(gc_mode()), GCCause::to_string(cause));
|
||||
_requested_gc_cause = cause;
|
||||
ml.notify();
|
||||
}
|
||||
}
|
||||
|
||||
bool ShenandoahGenerationalControlThread::preempt_old_marking(ShenandoahGeneration* generation) {
|
||||
|
||||
@ -135,16 +135,13 @@ private:
|
||||
// Return printable name for the given gc mode.
|
||||
static const char* gc_mode_name(GCMode mode);
|
||||
|
||||
// Takes the request lock and updates the requested cause and generation, then notifies the control thread.
|
||||
// The overloaded variant should be used when the _control_lock is already held.
|
||||
// These notify the control thread after updating _requested_gc_cause and (optionally) _requested_generation.
|
||||
// Updating the requested generation is not necessary for allocation failures nor when stopping the thread.
|
||||
void notify_control_thread(GCCause::Cause cause);
|
||||
void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause);
|
||||
void notify_control_thread(GCCause::Cause cause, ShenandoahGeneration* generation);
|
||||
void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation);
|
||||
|
||||
// Notifies the control thread, but does not update the requested cause or generation.
|
||||
// The overloaded variant should be used when the _control_lock is already held.
|
||||
void notify_cancellation(GCCause::Cause cause);
|
||||
void notify_cancellation(MonitorLocker& ml, GCCause::Cause cause);
|
||||
|
||||
// Configure the heap to age objects and regions if the aging period has elapsed.
|
||||
void maybe_set_aging_cycle();
|
||||
|
||||
|
||||
@ -44,13 +44,13 @@ public:
|
||||
void post_initialize_heuristics() override;
|
||||
|
||||
static ShenandoahGenerationalHeap* heap() {
|
||||
assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap");
|
||||
assert(ShenandoahCardBarrier, "Should have card barrier to use generational heap");
|
||||
CollectedHeap* heap = Universe::heap();
|
||||
return cast(heap);
|
||||
}
|
||||
|
||||
static ShenandoahGenerationalHeap* cast(CollectedHeap* heap) {
|
||||
assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap");
|
||||
assert(ShenandoahCardBarrier, "Should have card barrier to use generational heap");
|
||||
return checked_cast<ShenandoahGenerationalHeap*>(heap);
|
||||
}
|
||||
|
||||
|
||||
@ -149,6 +149,13 @@ bool ShenandoahRegulatorThread::start_global_cycle() const {
|
||||
|
||||
bool ShenandoahRegulatorThread::request_concurrent_gc(ShenandoahGeneration* generation) const {
|
||||
double now = os::elapsedTime();
|
||||
|
||||
// This call may find the control thread waiting on workers which have suspended
|
||||
// to allow a safepoint to run. If this regulator thread does not yield, the safepoint
|
||||
// will not run. The worker threads won't progress, the control thread won't progress,
|
||||
// and the regulator thread may never yield. Therefore, we leave the suspendible
|
||||
// thread set before making this call.
|
||||
SuspendibleThreadSetLeaver leaver;
|
||||
bool accepted = _control_thread->request_concurrent_gc(generation);
|
||||
if (LogTarget(Debug, gc, thread)::is_enabled() && accepted) {
|
||||
double wait_time = os::elapsedTime() - now;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, 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
|
||||
@ -32,6 +32,7 @@
|
||||
#include "jfr/recorder/repository/jfrEmergencyDump.hpp"
|
||||
#include "jfr/recorder/repository/jfrRepository.hpp"
|
||||
#include "jfr/recorder/service/jfrOptionSet.hpp"
|
||||
#include "jfr/recorder/service/jfrRecorderService.hpp"
|
||||
#include "jfr/support/jfrClassDefineEvent.hpp"
|
||||
#include "jfr/support/jfrKlassExtension.hpp"
|
||||
#include "jfr/support/jfrResolution.hpp"
|
||||
@ -43,6 +44,7 @@
|
||||
#include "runtime/java.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
|
||||
|
||||
bool Jfr::is_enabled() {
|
||||
return JfrRecorder::is_enabled();
|
||||
}
|
||||
@ -153,9 +155,9 @@ void Jfr::on_resolution(const Method* caller, const Method* target, TRAPS) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void Jfr::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown, bool halt) {
|
||||
void Jfr::on_vm_shutdown(bool exception_handler /* false */, bool halt /* false */, bool oom /* false */) {
|
||||
if (!halt && JfrRecorder::is_recording()) {
|
||||
JfrEmergencyDump::on_vm_shutdown(emit_old_object_samples, emit_event_shutdown);
|
||||
JfrEmergencyDump::on_vm_shutdown(exception_handler, oom);
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,6 +175,12 @@ bool Jfr::on_start_flight_recording_option(const JavaVMOption** option, char* de
|
||||
return JfrOptionSet::parse_start_flight_recording_option(option, delimiter);
|
||||
}
|
||||
|
||||
void Jfr::on_report_java_out_of_memory() {
|
||||
if (CrashOnOutOfMemoryError && JfrRecorder::is_recording()) {
|
||||
JfrRecorderService::emit_leakprofiler_events_on_oom();
|
||||
}
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS
|
||||
void Jfr::on_restoration(const Klass* k, JavaThread* jt) {
|
||||
assert(k != nullptr, "invariant");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2026, 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
|
||||
@ -71,7 +71,7 @@ class Jfr : AllStatic {
|
||||
static void on_resolution(const Method* caller, const Method* target, TRAPS);
|
||||
static void on_java_thread_start(JavaThread* starter, JavaThread* startee);
|
||||
static void on_set_current_thread(JavaThread* jt, oop thread);
|
||||
static void on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown, bool halt = false);
|
||||
static void on_vm_shutdown(bool exception_handler = false, bool halt = false, bool oom = false);
|
||||
static void on_vm_error_report(outputStream* st);
|
||||
static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
|
||||
static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
|
||||
@ -79,6 +79,7 @@ class Jfr : AllStatic {
|
||||
static void initialize_main_thread(JavaThread* jt);
|
||||
static bool has_sample_request(JavaThread* jt);
|
||||
static void check_and_process_sample_request(JavaThread* jt);
|
||||
static void on_report_java_out_of_memory();
|
||||
CDS_ONLY(static void on_restoration(const Klass* k, JavaThread* jt);)
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2026, 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
|
||||
@ -364,8 +364,7 @@ JVM_ENTRY_NO_ENV(void, jfr_set_force_instrumentation(JNIEnv* env, jclass jvm, jb
|
||||
JVM_END
|
||||
|
||||
NO_TRANSITION(void, jfr_emit_old_object_samples(JNIEnv* env, jclass jvm, jlong cutoff_ticks, jboolean emit_all, jboolean skip_bfs))
|
||||
JfrRecorderService service;
|
||||
service.emit_leakprofiler_events(cutoff_ticks, emit_all == JNI_TRUE, skip_bfs == JNI_TRUE);
|
||||
JfrRecorderService::emit_leakprofiler_events(cutoff_ticks, emit_all == JNI_TRUE, skip_bfs == JNI_TRUE);
|
||||
NO_TRANSITION_END
|
||||
|
||||
JVM_ENTRY_NO_ENV(void, jfr_exclude_thread(JNIEnv* env, jclass jvm, jobject t))
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026, 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
|
||||
@ -232,41 +232,50 @@ void JfrSamplerThread::task_stacktrace(JfrSampleRequestType type, JavaThread** l
|
||||
JavaThread* start = nullptr;
|
||||
elapsedTimer sample_time;
|
||||
sample_time.start();
|
||||
ThreadsListHandle tlh;
|
||||
// Resolve a sample session relative start position index into the thread list array.
|
||||
// In cases where the last sampled thread is null or not-null but stale, find_index() returns -1.
|
||||
_cur_index = tlh.list()->find_index_of_JavaThread(*last_thread);
|
||||
JavaThread* current = _cur_index != -1 ? *last_thread : nullptr;
|
||||
{
|
||||
/*
|
||||
* Take the Threads_lock for three purposes:
|
||||
*
|
||||
* 1) Avoid sampling right through a safepoint,
|
||||
* which could result in touching oops in case of virtual threads.
|
||||
* 2) Prevent JFR from issuing an epoch rotation while the sampler thread
|
||||
* is actively processing a thread in state native, as both threads are outside the safepoint protocol.
|
||||
* 3) Some operating systems (BSD / Mac) require a process lock when sending a signal with pthread_kill.
|
||||
* Holding the Threads_lock prevents a JavaThread from calling os::create_thread(), which also takes the process lock.
|
||||
* In a sense, we provide a coarse signal mask, so we can always send the resume signal.
|
||||
*/
|
||||
MutexLocker tlock(Threads_lock);
|
||||
ThreadsListHandle tlh;
|
||||
// Resolve a sample session relative start position index into the thread list array.
|
||||
// In cases where the last sampled thread is null or not-null but stale, find_index() returns -1.
|
||||
_cur_index = tlh.list()->find_index_of_JavaThread(*last_thread);
|
||||
JavaThread* current = _cur_index != -1 ? *last_thread : nullptr;
|
||||
|
||||
while (num_samples < sample_limit) {
|
||||
current = next_thread(tlh.list(), start, current);
|
||||
if (current == nullptr) {
|
||||
break;
|
||||
}
|
||||
if (is_excluded(current)) {
|
||||
continue;
|
||||
}
|
||||
if (start == nullptr) {
|
||||
start = current; // remember the thread where we started to attempt sampling
|
||||
}
|
||||
bool success;
|
||||
if (JAVA_SAMPLE == type) {
|
||||
success = sample_java_thread(current);
|
||||
} else {
|
||||
assert(type == NATIVE_SAMPLE, "invariant");
|
||||
success = sample_native_thread(current);
|
||||
}
|
||||
if (success) {
|
||||
num_samples++;
|
||||
}
|
||||
if (SafepointSynchronize::is_at_safepoint()) {
|
||||
// For _thread_in_native, we cannot get the Threads_lock.
|
||||
// For _thread_in_Java, well, there are none.
|
||||
break;
|
||||
while (num_samples < sample_limit) {
|
||||
current = next_thread(tlh.list(), start, current);
|
||||
if (current == nullptr) {
|
||||
break;
|
||||
}
|
||||
if (is_excluded(current)) {
|
||||
continue;
|
||||
}
|
||||
if (start == nullptr) {
|
||||
start = current; // remember the thread where we started to attempt sampling
|
||||
}
|
||||
bool success;
|
||||
if (JAVA_SAMPLE == type) {
|
||||
success = sample_java_thread(current);
|
||||
} else {
|
||||
assert(type == NATIVE_SAMPLE, "invariant");
|
||||
success = sample_native_thread(current);
|
||||
}
|
||||
if (success) {
|
||||
num_samples++;
|
||||
}
|
||||
}
|
||||
|
||||
*last_thread = current; // remember the thread we last attempted to sample
|
||||
}
|
||||
|
||||
*last_thread = current; // remember the thread we last attempted to sample
|
||||
sample_time.stop();
|
||||
log_trace(jfr)("JFR thread sampling done in %3.7f secs with %d java %d native samples",
|
||||
sample_time.seconds(), type == JAVA_SAMPLE ? num_samples : 0, type == NATIVE_SAMPLE ? num_samples : 0);
|
||||
@ -297,6 +306,7 @@ class OSThreadSampler : public SuspendedThreadTask {
|
||||
// Sampling a thread in state _thread_in_Java
|
||||
// involves a platform-specific thread suspend and CPU context retrieval.
|
||||
bool JfrSamplerThread::sample_java_thread(JavaThread* jt) {
|
||||
assert_lock_strong(Threads_lock);
|
||||
if (jt->thread_state() != _thread_in_Java) {
|
||||
return false;
|
||||
}
|
||||
@ -328,6 +338,7 @@ static JfrSamplerThread* _sampler_thread = nullptr;
|
||||
// without thread suspension and CPU context retrieval,
|
||||
// if we carefully order the loads of the thread state.
|
||||
bool JfrSamplerThread::sample_native_thread(JavaThread* jt) {
|
||||
assert_lock_strong(Threads_lock);
|
||||
if (jt->thread_state() != _thread_in_native) {
|
||||
return false;
|
||||
}
|
||||
@ -343,24 +354,14 @@ bool JfrSamplerThread::sample_native_thread(JavaThread* jt) {
|
||||
|
||||
SafepointMechanism::arm_local_poll_release(jt);
|
||||
|
||||
// Take the Threads_lock for two purposes:
|
||||
// 1) Avoid sampling through a safepoint which could result
|
||||
// in touching oops in case of virtual threads.
|
||||
// 2) Prevent JFR from issuing an epoch rotation while the sampler thread
|
||||
// is actively processing a thread in native, as both threads are now
|
||||
// outside the safepoint protocol.
|
||||
|
||||
// OrderAccess::fence() as part of acquiring the lock prevents loads from floating up.
|
||||
JfrMutexTryLock threads_lock(Threads_lock);
|
||||
|
||||
if (!threads_lock.acquired() || !jt->has_last_Java_frame()) {
|
||||
// Remove the native sample request and release the potentially waiting thread.
|
||||
JfrSampleMonitor jsm(tl);
|
||||
return false;
|
||||
// Separate the arming of the poll (above) from the reading of JavaThread state (below).
|
||||
if (UseSystemMemoryBarrier) {
|
||||
SystemMemoryBarrier::emit();
|
||||
} else {
|
||||
OrderAccess::fence();
|
||||
}
|
||||
|
||||
if (jt->thread_state() != _thread_in_native) {
|
||||
assert_lock_strong(Threads_lock);
|
||||
if (jt->thread_state() != _thread_in_native || !jt->has_last_Java_frame()) {
|
||||
JfrSampleMonitor jsm(tl);
|
||||
if (jsm.is_waiting()) {
|
||||
// The thread has already returned from native,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026, 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,8 @@
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/vmOperations.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
||||
@ -460,15 +462,6 @@ static void release_locks(Thread* thread) {
|
||||
assert(thread != nullptr, "invariant");
|
||||
assert(!thread->is_Java_thread() || JavaThread::cast(thread)->thread_state() == _thread_in_vm, "invariant");
|
||||
|
||||
#ifdef ASSERT
|
||||
Mutex* owned_lock = thread->owned_locks();
|
||||
while (owned_lock != nullptr) {
|
||||
Mutex* next = owned_lock->next();
|
||||
owned_lock->unlock();
|
||||
owned_lock = next;
|
||||
}
|
||||
#endif // ASSERT
|
||||
|
||||
if (Threads_lock->owned_by_self()) {
|
||||
Threads_lock->unlock();
|
||||
}
|
||||
@ -550,17 +543,14 @@ class JavaThreadInVMAndNative : public StackObj {
|
||||
}
|
||||
};
|
||||
|
||||
static void post_events(bool emit_old_object_samples, bool emit_event_shutdown, Thread* thread) {
|
||||
if (emit_old_object_samples) {
|
||||
LeakProfiler::emit_events(max_jlong, false, false);
|
||||
}
|
||||
if (emit_event_shutdown) {
|
||||
static void post_events(bool exception_handler, bool oom, Thread * thread) {
|
||||
if (exception_handler) {
|
||||
EventShutdown e;
|
||||
e.set_reason("VM Error");
|
||||
e.set_reason(oom ? "CrashOnOutOfMemoryError" : "VM Error");
|
||||
e.commit();
|
||||
}
|
||||
EventDumpReason event;
|
||||
event.set_reason(emit_old_object_samples ? "Out of Memory" : "Crash");
|
||||
event.set_reason(exception_handler && oom ? "CrashOnOutOfMemoryError" : exception_handler ? "Crash" : "Out of Memory");
|
||||
event.set_recordingId(-1);
|
||||
event.commit();
|
||||
}
|
||||
@ -594,20 +584,40 @@ static bool guard_reentrancy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void JfrEmergencyDump::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown) {
|
||||
void JfrEmergencyDump::on_vm_shutdown(bool exception_handler, bool oom) {
|
||||
if (!guard_reentrancy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Thread* const thread = Thread::current_or_null_safe();
|
||||
assert(thread != nullptr, "invariant");
|
||||
if (thread->is_Watcher_thread()) {
|
||||
log_info(jfr, system)("The Watcher thread crashed so no jfr emergency dump will be generated.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure a JavaThread is _thread_in_vm when we make this call
|
||||
JavaThreadInVMAndNative jtivm(thread);
|
||||
post_events(exception_handler, oom, thread);
|
||||
|
||||
if (thread->is_Watcher_thread()) {
|
||||
// We cannot attempt an emergency dump using the Watcher thread
|
||||
// because we rely on the WatcherThread task "is_error_reported()",
|
||||
// to exit the VM after a hardcoded timeout, should the relatively
|
||||
// risky operation of an emergency dump fail (deadlock, livelock).
|
||||
log_warning(jfr, system)
|
||||
("The Watcher thread crashed so no jfr emergency dump will be generated.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread->is_VM_thread()) {
|
||||
const VM_Operation* const operation = VMThread::vm_operation();
|
||||
if (operation != nullptr && operation->type() == VM_Operation::VMOp_JFROldObject) {
|
||||
// We will not be able to issue a rotation because the rotation lock
|
||||
// is held by the JFR Recorder Thread that issued the VM_Operation.
|
||||
log_warning(jfr, system)
|
||||
("The VM Thread crashed as part of emitting leak profiler events so no jfr emergency dump will be generated.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
release_locks(thread);
|
||||
post_events(emit_old_object_samples, emit_event_shutdown, thread);
|
||||
|
||||
// if JavaThread, transition to _thread_in_native to issue a final flushpoint
|
||||
NoHandleMark nhm;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2026, 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
|
||||
@ -39,7 +39,7 @@ class JfrEmergencyDump : AllStatic {
|
||||
static const char* chunk_path(const char* repository_path);
|
||||
static void on_vm_error(const char* repository_path);
|
||||
static void on_vm_error_report(outputStream* st, const char* repository_path);
|
||||
static void on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown);
|
||||
static void on_vm_shutdown(bool exception_handler, bool oom);
|
||||
};
|
||||
|
||||
#endif // SHARE_JFR_RECORDER_REPOSITORY_JFREMERGENCYDUMP_HPP
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2026, 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
|
||||
@ -34,7 +34,8 @@
|
||||
(MSGBIT(MSG_START)) | \
|
||||
(MSGBIT(MSG_CLONE_IN_MEMORY)) | \
|
||||
(MSGBIT(MSG_VM_ERROR)) | \
|
||||
(MSGBIT(MSG_FLUSHPOINT)) \
|
||||
(MSGBIT(MSG_FLUSHPOINT)) | \
|
||||
(MSGBIT(MSG_EMIT_LEAKP_REFCHAINS)) \
|
||||
)
|
||||
|
||||
static JfrPostBox* _instance = nullptr;
|
||||
@ -165,7 +166,7 @@ void JfrPostBox::notify_waiters() {
|
||||
assert(JfrMsg_lock->owned_by_self(), "incrementing _msg_handled_serial is protected by JfrMsg_lock.");
|
||||
// Update made visible on release of JfrMsg_lock via fence instruction in Monitor::IUnlock.
|
||||
++_msg_handled_serial;
|
||||
JfrMsg_lock->notify();
|
||||
JfrMsg_lock->notify_all();
|
||||
}
|
||||
|
||||
// safeguard to ensure no threads are left waiting
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2026, 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
|
||||
@ -43,6 +43,7 @@ enum JFR_Msg {
|
||||
MSG_SHUTDOWN,
|
||||
MSG_VM_ERROR,
|
||||
MSG_FLUSHPOINT,
|
||||
MSG_EMIT_LEAKP_REFCHAINS,
|
||||
MSG_NO_OF_MSGS
|
||||
};
|
||||
|
||||
@ -51,23 +52,25 @@ enum JFR_Msg {
|
||||
*
|
||||
* Synchronous messages (posting thread waits for message completion):
|
||||
*
|
||||
* MSG_CLONE_IN_MEMORY (0) ; MSGBIT(MSG_CLONE_IN_MEMORY) == (1 << 0) == 0x1
|
||||
* MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2
|
||||
* MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4
|
||||
* MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8
|
||||
* MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100
|
||||
* MSG_FLUSHPOINT (9) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0x9) == 0x200
|
||||
* MSG_CLONE_IN_MEMORY (0) ; MSGBIT(MSG_CLONE_IN_MEMORY) == (1 << 0) == 0x1
|
||||
* MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2
|
||||
* MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4
|
||||
* MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8
|
||||
* MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100
|
||||
* MSG_FLUSHPOINT (9) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0x9) == 0x200
|
||||
* MSG_EMIT_LEAKP_REFCHAINS (10); MSGBIT(MSG_EMIT_LEAKP_REFCHAINS) == (1 << 0xa) == 0x400
|
||||
*
|
||||
* Asynchronous messages (posting thread returns immediately upon deposit):
|
||||
*
|
||||
* MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10
|
||||
* MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20
|
||||
* MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40
|
||||
* MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80
|
||||
* MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10
|
||||
* MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20
|
||||
* MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40
|
||||
* MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80
|
||||
*/
|
||||
|
||||
class JfrPostBox : public JfrCHeapObj {
|
||||
friend class JfrRecorder;
|
||||
friend class JfrRecorderService;
|
||||
public:
|
||||
void post(JFR_Msg msg);
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2026, 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
|
||||
@ -55,6 +55,7 @@
|
||||
#include "runtime/safepoint.hpp"
|
||||
#include "runtime/vmOperations.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
|
||||
// incremented on each flushpoint
|
||||
static u8 flushpoint_id = 0;
|
||||
@ -391,6 +392,7 @@ class JfrSafepointWriteVMOperation : public VM_Operation {
|
||||
JfrRecorderService::JfrRecorderService() :
|
||||
_checkpoint_manager(JfrCheckpointManager::instance()),
|
||||
_chunkwriter(JfrRepository::chunkwriter()),
|
||||
_post_box(JfrPostBox::instance()),
|
||||
_repository(JfrRepository::instance()),
|
||||
_stack_trace_repository(JfrStackTraceRepository::instance()),
|
||||
_storage(JfrStorage::instance()),
|
||||
@ -670,17 +672,173 @@ void JfrRecorderService::evaluate_chunk_size_for_rotation() {
|
||||
JfrChunkRotation::evaluate(_chunkwriter);
|
||||
}
|
||||
|
||||
void JfrRecorderService::emit_leakprofiler_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs) {
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(JavaThread::current()));
|
||||
// Take the rotation lock to exclude flush() during event emits. This is because event emit
|
||||
// also creates a number checkpoint events. Those checkpoint events require a future typeset checkpoint
|
||||
// event for completeness, i.e. to be generated before being flushed to a segment.
|
||||
// The upcoming flush() or rotation() after event emit completes this typeset checkpoint
|
||||
// and serializes all event emit checkpoint events to the same segment.
|
||||
JfrRotationLock lock;
|
||||
// Take the rotation lock before the transition.
|
||||
JavaThread* current_thread = JavaThread::current();
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current_thread));
|
||||
ThreadInVMfromNative transition(current_thread);
|
||||
LeakProfiler::emit_events(cutoff_ticks, emit_all, skip_bfs);
|
||||
// LeakProfiler event serialization support.
|
||||
|
||||
struct JfrLeakProfilerEmitRequest {
|
||||
int64_t cutoff_ticks;
|
||||
bool emit_all;
|
||||
bool skip_bfs;
|
||||
bool oom;
|
||||
};
|
||||
|
||||
typedef GrowableArrayCHeap<JfrLeakProfilerEmitRequest, mtTracing> JfrLeakProfilerEmitRequestQueue;
|
||||
static JfrLeakProfilerEmitRequestQueue* _queue = nullptr;
|
||||
constexpr const static int64_t _no_path_to_gc_roots = 0;
|
||||
static bool _oom_emit_request_posted = false;
|
||||
static bool _oom_emit_request_delivered = false;
|
||||
|
||||
static inline bool exclude_paths_to_gc_roots(int64_t cutoff_ticks) {
|
||||
return cutoff_ticks <= _no_path_to_gc_roots;
|
||||
}
|
||||
|
||||
static void enqueue(const JfrLeakProfilerEmitRequest& request) {
|
||||
assert(JfrRotationLock::is_owner(), "invariant");
|
||||
if (_queue == nullptr) {
|
||||
_queue = new JfrLeakProfilerEmitRequestQueue(4);
|
||||
}
|
||||
assert(_queue != nullptr, "invariant");
|
||||
assert(!_oom_emit_request_posted, "invariant");
|
||||
if (request.oom) {
|
||||
_oom_emit_request_posted = true;
|
||||
}
|
||||
_queue->append(request);
|
||||
}
|
||||
|
||||
static JfrLeakProfilerEmitRequest dequeue() {
|
||||
assert(JfrRotationLock::is_owner(), "invariant");
|
||||
assert(_queue != nullptr, "invariant");
|
||||
assert(_queue->is_nonempty(), "invariant");
|
||||
const JfrLeakProfilerEmitRequest& request = _queue->first();
|
||||
_queue->remove_at(0);
|
||||
return request;
|
||||
}
|
||||
|
||||
// This version of emit excludes path-to-gc-roots, i.e. it skips reference chains.
|
||||
static void emit_leakprofiler_events(bool emit_all, bool skip_bfs, JavaThread* jt) {
|
||||
assert(jt != nullptr, "invariant");
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
|
||||
// Take the rotation lock to exclude flush() during event emits. This is because the event emit operation
|
||||
// also creates a number of checkpoint events. Those checkpoint events require a future typeset checkpoint
|
||||
// event for completeness, i.e., to be generated before being flushed to a segment.
|
||||
// The upcoming flush() or rotation() after event emit completes this typeset checkpoint
|
||||
// and serializes all checkpoint events to the same segment.
|
||||
JfrRotationLock lock;
|
||||
// Take the rotation lock before the thread transition, to avoid blocking safepoints.
|
||||
if (_oom_emit_request_posted) {
|
||||
// A request to emit leakprofiler events in response to CrashOnOutOfMemoryError
|
||||
// is pending or has already been completed. We are about to crash at any time now.
|
||||
assert(CrashOnOutOfMemoryError, "invariant");
|
||||
return;
|
||||
}
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt));
|
||||
ThreadInVMfromNative transition(jt);
|
||||
// Since we are not requesting path-to-gc-roots, i.e., reference chains, we need not issue a VM_Operation.
|
||||
// Therefore, we can let the requesting thread process the request directly, since it already holds the requisite lock.
|
||||
LeakProfiler::emit_events(_no_path_to_gc_roots, emit_all, skip_bfs);
|
||||
}
|
||||
|
||||
void JfrRecorderService::transition_and_post_leakprofiler_emit_msg(JavaThread* jt) {
|
||||
assert(jt != nullptr, "invariant");
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);)
|
||||
assert(!JfrRotationLock::is_owner(), "invariant");
|
||||
// Transition to _thread_in_VM and post a synchronous message to the JFR Recorder Thread
|
||||
// for it to process our enqueued request, which includes paths-to-gc-roots, i.e., reference chains.
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt));
|
||||
ThreadInVMfromNative transition(jt);
|
||||
_post_box.post(MSG_EMIT_LEAKP_REFCHAINS);
|
||||
}
|
||||
|
||||
// This version of emit includes path-to-gc-roots, i.e., it includes in the request traversing of reference chains.
|
||||
// Traversing reference chains is performed as part of a VM_Operation, and we initiate it from the JFR Recorder Thread.
|
||||
// Because multiple threads can concurrently report_on_java_out_of_memory(), having them all post a synchronous JFR msg,
|
||||
// they rendezvous at a safepoint in a convenient state, ThreadBlockInVM. This mechanism prevents any thread from racing past
|
||||
// this point and begin executing VMError::report_and_die(), until at least one oom request has been delivered.
|
||||
void JfrRecorderService::emit_leakprofiler_events_paths_to_gc_roots(int64_t cutoff_ticks,
|
||||
bool emit_all,
|
||||
bool skip_bfs,
|
||||
bool oom,
|
||||
JavaThread* jt) {
|
||||
assert(jt != nullptr, "invariant");
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);)
|
||||
assert(!exclude_paths_to_gc_roots(cutoff_ticks), "invariant");
|
||||
|
||||
{
|
||||
JfrRotationLock lock;
|
||||
// Take the rotation lock to read and post a request for the JFR Recorder Thread.
|
||||
if (_oom_emit_request_posted) {
|
||||
if (!oom) {
|
||||
// A request to emit leakprofiler events in response to CrashOnOutOfMemoryError
|
||||
// is pending or has already been completed. We are about to crash at any time now.
|
||||
assert(CrashOnOutOfMemoryError, "invariant");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
assert(!_oom_emit_request_posted, "invariant");
|
||||
JfrLeakProfilerEmitRequest request = { cutoff_ticks, emit_all, skip_bfs, oom };
|
||||
enqueue(request);
|
||||
}
|
||||
}
|
||||
JfrRecorderService service;
|
||||
service.transition_and_post_leakprofiler_emit_msg(jt);
|
||||
}
|
||||
|
||||
// Leakprofiler serialization request, the jdk.jfr.internal.JVM.emitOldObjectSamples() Java entry point.
|
||||
void JfrRecorderService::emit_leakprofiler_events(int64_t cutoff_ticks,
|
||||
bool emit_all,
|
||||
bool skip_bfs) {
|
||||
JavaThread* const jt = JavaThread::current();
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);)
|
||||
if (exclude_paths_to_gc_roots(cutoff_ticks)) {
|
||||
::emit_leakprofiler_events(emit_all, skip_bfs, jt);
|
||||
return;
|
||||
}
|
||||
emit_leakprofiler_events_paths_to_gc_roots(cutoff_ticks, emit_all, skip_bfs, /* oom */ false, jt);
|
||||
}
|
||||
|
||||
// Leakprofiler serialization request, the report_on_java_out_of_memory VM entry point.
|
||||
void JfrRecorderService::emit_leakprofiler_events_on_oom() {
|
||||
assert(CrashOnOutOfMemoryError, "invariant");
|
||||
if (EventOldObjectSample::is_enabled()) {
|
||||
JavaThread* const jt = JavaThread::current();
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt);)
|
||||
ThreadToNativeFromVM transition(jt);
|
||||
emit_leakprofiler_events_paths_to_gc_roots(max_jlong, false, false, /* oom */ true, jt);
|
||||
}
|
||||
}
|
||||
|
||||
// The worker routine for the JFR Recorder Thread when processing MSG_EMIT_LEAKP_REFCHAINS messages.
|
||||
void JfrRecorderService::emit_leakprofiler_events() {
|
||||
JavaThread* const jt = JavaThread::current();
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
|
||||
// Take the rotation lock before the transition.
|
||||
JfrRotationLock lock;
|
||||
if (_oom_emit_request_delivered) {
|
||||
// A request to emit leakprofiler events in response to CrashOnOutOfMemoryError
|
||||
// has already been completed. We are about to crash at any time now.
|
||||
assert(_oom_emit_request_posted, "invariant");
|
||||
assert(CrashOnOutOfMemoryError, "invariant");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(_queue->is_nonempty(), "invariant");
|
||||
|
||||
{
|
||||
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt));
|
||||
ThreadInVMfromNative transition(jt);
|
||||
while (_queue->is_nonempty()) {
|
||||
const JfrLeakProfilerEmitRequest& request = dequeue();
|
||||
LeakProfiler::emit_events(request.cutoff_ticks, request.emit_all, request.skip_bfs);
|
||||
if (_oom_emit_request_posted && request.oom) {
|
||||
assert(CrashOnOutOfMemoryError, "invariant");
|
||||
_oom_emit_request_delivered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If processing involved an out-of-memory request, issue an immediate flush operation.
|
||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
|
||||
if (_chunkwriter.is_valid() && _oom_emit_request_delivered) {
|
||||
invoke_flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2026, 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,19 +27,23 @@
|
||||
|
||||
#include "jfr/utilities/jfrAllocation.hpp"
|
||||
|
||||
class JavaThread;
|
||||
class JfrCheckpointManager;
|
||||
class JfrChunkWriter;
|
||||
class JfrPostBox;
|
||||
class JfrRepository;
|
||||
class JfrStackTraceRepository;
|
||||
class JfrStorage;
|
||||
class JfrStringPool;
|
||||
|
||||
class JfrRecorderService : public StackObj {
|
||||
friend class Jfr;
|
||||
friend class JfrSafepointClearVMOperation;
|
||||
friend class JfrSafepointWriteVMOperation;
|
||||
private:
|
||||
JfrCheckpointManager& _checkpoint_manager;
|
||||
JfrChunkWriter& _chunkwriter;
|
||||
JfrPostBox& _post_box;
|
||||
JfrRepository& _repository;
|
||||
JfrStackTraceRepository& _stack_trace_repository;
|
||||
JfrStorage& _storage;
|
||||
@ -64,6 +68,14 @@ class JfrRecorderService : public StackObj {
|
||||
void invoke_safepoint_write();
|
||||
void post_safepoint_write();
|
||||
|
||||
void transition_and_post_leakprofiler_emit_msg(JavaThread* jt);
|
||||
|
||||
static void emit_leakprofiler_events_on_oom();
|
||||
static void emit_leakprofiler_events_paths_to_gc_roots(int64_t cutoff_ticks,
|
||||
bool emit_all,
|
||||
bool skip_bfs,
|
||||
bool oom,
|
||||
JavaThread* jt);
|
||||
public:
|
||||
JfrRecorderService();
|
||||
void start();
|
||||
@ -72,8 +84,12 @@ class JfrRecorderService : public StackObj {
|
||||
void flushpoint();
|
||||
void process_full_buffers();
|
||||
void evaluate_chunk_size_for_rotation();
|
||||
void emit_leakprofiler_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs);
|
||||
void emit_leakprofiler_events();
|
||||
|
||||
static bool is_recording();
|
||||
static void emit_leakprofiler_events(int64_t cutoff_ticks,
|
||||
bool emit_all,
|
||||
bool skip_bfs);
|
||||
};
|
||||
|
||||
#endif // SHARE_JFR_RECORDER_SERVICE_JFRRECORDERSERVICE_HPP
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026, 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
|
||||
@ -44,6 +44,7 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
|
||||
#define ROTATE (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)))
|
||||
#define FLUSHPOINT (msgs & (MSGBIT(MSG_FLUSHPOINT)))
|
||||
#define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER)))
|
||||
#define LEAKPROFILER_REFCHAINS (msgs & MSGBIT(MSG_EMIT_LEAKP_REFCHAINS))
|
||||
|
||||
JfrPostBox& post_box = JfrRecorderThreadEntry::post_box();
|
||||
log_debug(jfr, system)("Recorder thread STARTED");
|
||||
@ -70,6 +71,9 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
|
||||
if (PROCESS_FULL_BUFFERS) {
|
||||
service.process_full_buffers();
|
||||
}
|
||||
if (LEAKPROFILER_REFCHAINS) {
|
||||
service.emit_leakprofiler_events();
|
||||
}
|
||||
// Check amount of data written to chunk already
|
||||
// if it warrants asking for a new chunk.
|
||||
service.evaluate_chunk_size_for_rotation();
|
||||
@ -98,5 +102,5 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
|
||||
#undef ROTATE
|
||||
#undef FLUSHPOINT
|
||||
#undef PROCESS_FULL_BUFFERS
|
||||
#undef SCAVENGE
|
||||
#undef LEAKPROFILER_REFCHAINS
|
||||
}
|
||||
|
||||
@ -1031,6 +1031,15 @@ bool CallNode::is_call_to_arraycopystub() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CallNode::is_call_to_multianewarray_stub() const {
|
||||
if (_name != nullptr &&
|
||||
strstr(_name, "multianewarray") != nullptr &&
|
||||
strstr(_name, "C2 runtime") != nullptr) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
uint CallJavaNode::size_of() const { return sizeof(*this); }
|
||||
bool CallJavaNode::cmp( const Node &n ) const {
|
||||
|
||||
@ -758,6 +758,7 @@ public:
|
||||
virtual uint match_edge(uint idx) const;
|
||||
|
||||
bool is_call_to_arraycopystub() const;
|
||||
bool is_call_to_multianewarray_stub() const;
|
||||
|
||||
virtual void copy_call_debug_info(PhaseIterGVN* phase, SafePointNode* sfpt) {}
|
||||
|
||||
|
||||
@ -1112,8 +1112,6 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if( !ti->is_con() ) return nullptr;
|
||||
jint con = ti->get_con();
|
||||
|
||||
Node *hook = new Node(1);
|
||||
|
||||
// First, special check for modulo 2^k-1
|
||||
if( con >= 0 && con < max_jint && is_power_of_2(con+1) ) {
|
||||
uint k = exact_log2(con+1); // Extract k
|
||||
@ -1129,7 +1127,9 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
Node *x = in(1); // Value being mod'd
|
||||
Node *divisor = in(2); // Also is mask
|
||||
|
||||
hook->init_req(0, x); // Add a use to x to prevent him from dying
|
||||
// Add a use to x to prevent it from dying
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, x);
|
||||
// Generate code to reduce X rapidly to nearly 2^k-1.
|
||||
for( int i = 0; i < trip_count; i++ ) {
|
||||
Node *xl = phase->transform( new AndINode(x,divisor) );
|
||||
@ -1185,6 +1185,7 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
// Save in(1) so that it cannot be changed or deleted
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, in(1));
|
||||
|
||||
// Divide using the transform from DivI to MulL
|
||||
@ -1407,8 +1408,6 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if( !tl->is_con() ) return nullptr;
|
||||
jlong con = tl->get_con();
|
||||
|
||||
Node *hook = new Node(1);
|
||||
|
||||
// Expand mod
|
||||
if(con >= 0 && con < max_jlong && is_power_of_2(con + 1)) {
|
||||
uint k = log2i_exact(con + 1); // Extract k
|
||||
@ -1426,13 +1425,15 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
Node *x = in(1); // Value being mod'd
|
||||
Node *divisor = in(2); // Also is mask
|
||||
|
||||
hook->init_req(0, x); // Add a use to x to prevent him from dying
|
||||
// Add a use to x to prevent it from dying
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, x);
|
||||
// Generate code to reduce X rapidly to nearly 2^k-1.
|
||||
for( int i = 0; i < trip_count; i++ ) {
|
||||
Node *xl = phase->transform( new AndLNode(x,divisor) );
|
||||
Node *xh = phase->transform( new RShiftLNode(x,phase->intcon(k)) ); // Must be signed
|
||||
x = phase->transform( new AddLNode(xh,xl) );
|
||||
hook->set_req(0, x); // Add a use to x to prevent him from dying
|
||||
hook->set_req(0, x); // Add a use to x to prevent it from dying
|
||||
}
|
||||
|
||||
// Generate sign-fixup code. Was original value positive?
|
||||
@ -1482,6 +1483,8 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
// Save in(1) so that it cannot be changed or deleted
|
||||
// Add a use to x to prevent him from dying
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, in(1));
|
||||
|
||||
// Divide using the transform from DivL to MulL
|
||||
|
||||
@ -2066,8 +2066,7 @@ void ConnectionGraph::add_call_node(CallNode* call) {
|
||||
// Use bytecode estimator to record whether the call's return value escapes.
|
||||
ciMethod* meth = call->as_CallJava()->method();
|
||||
if (meth == nullptr) {
|
||||
const char* name = call->as_CallStaticJava()->_name;
|
||||
assert(strncmp(name, "C2 Runtime multianewarray", 25) == 0, "TODO: add failed case check");
|
||||
assert(call->as_CallStaticJava()->is_call_to_multianewarray_stub(), "TODO: add failed case check");
|
||||
// Returns a newly allocated non-escaped object.
|
||||
add_java_object(call, PointsToNode::NoEscape);
|
||||
set_not_scalar_replaceable(ptnode_adr(call_idx) NOT_PRODUCT(COMMA "is result of multinewarray"));
|
||||
@ -2775,8 +2774,7 @@ int ConnectionGraph::find_init_values_phantom(JavaObjectNode* pta) {
|
||||
assert(pta->arraycopy_dst() || alloc->as_CallStaticJava(), "sanity");
|
||||
#ifdef ASSERT
|
||||
if (!pta->arraycopy_dst() && alloc->as_CallStaticJava()->method() == nullptr) {
|
||||
const char* name = alloc->as_CallStaticJava()->_name;
|
||||
assert(strncmp(name, "C2 Runtime multianewarray", 25) == 0, "sanity");
|
||||
assert(alloc->as_CallStaticJava()->is_call_to_multianewarray_stub(), "sanity");
|
||||
}
|
||||
#endif
|
||||
// Non-escaped allocation returned from Java or runtime call have unknown values in fields.
|
||||
|
||||
@ -3913,7 +3913,6 @@ const Type* SCMemProjNode::Value(PhaseGVN* phase) const
|
||||
LoadStoreNode::LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required )
|
||||
: Node(required),
|
||||
_type(rt),
|
||||
_adr_type(at),
|
||||
_barrier_data(0)
|
||||
{
|
||||
init_req(MemNode::Control, c );
|
||||
@ -3921,6 +3920,7 @@ LoadStoreNode::LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const Ty
|
||||
init_req(MemNode::Address, adr);
|
||||
init_req(MemNode::ValueIn, val);
|
||||
init_class_id(Class_LoadStore);
|
||||
DEBUG_ONLY(_adr_type = at; adr_type();)
|
||||
}
|
||||
|
||||
//------------------------------Value-----------------------------------------
|
||||
@ -3944,6 +3944,11 @@ const Type* LoadStoreNode::Value(PhaseGVN* phase) const {
|
||||
return bottom_type();
|
||||
}
|
||||
|
||||
const TypePtr* LoadStoreNode::adr_type() const {
|
||||
const TypePtr* cross_check = DEBUG_ONLY(_adr_type) NOT_DEBUG(nullptr);
|
||||
return MemNode::calculate_adr_type(in(MemNode::Address)->bottom_type(), cross_check);
|
||||
}
|
||||
|
||||
uint LoadStoreNode::ideal_reg() const {
|
||||
return _type->ideal_reg();
|
||||
}
|
||||
|
||||
@ -797,11 +797,6 @@ public:
|
||||
virtual int Opcode() const;
|
||||
virtual bool is_CFG() const { return false; }
|
||||
virtual const Type *bottom_type() const {return Type::MEMORY;}
|
||||
virtual const TypePtr *adr_type() const {
|
||||
Node* ctrl = in(0);
|
||||
if (ctrl == nullptr) return nullptr; // node is dead
|
||||
return ctrl->in(MemNode::Memory)->adr_type();
|
||||
}
|
||||
virtual uint ideal_reg() const { return 0;} // memory projections don't have a register
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
#ifndef PRODUCT
|
||||
@ -814,9 +809,11 @@ public:
|
||||
class LoadStoreNode : public Node {
|
||||
private:
|
||||
const Type* const _type; // What kind of value is loaded?
|
||||
const TypePtr* _adr_type; // What kind of memory is being addressed?
|
||||
uint8_t _barrier_data; // Bit field with barrier information
|
||||
virtual uint size_of() const; // Size is bigger
|
||||
#ifdef ASSERT
|
||||
const TypePtr* _adr_type; // What kind of memory is being addressed?
|
||||
#endif // ASSERT
|
||||
public:
|
||||
LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required );
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
@ -824,7 +821,7 @@ public:
|
||||
|
||||
virtual const Type *bottom_type() const { return _type; }
|
||||
virtual uint ideal_reg() const;
|
||||
virtual const class TypePtr *adr_type() const { return _adr_type; } // returns bottom_type of address
|
||||
virtual const TypePtr* adr_type() const;
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
|
||||
bool result_not_used() const;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2007, 2026, 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
|
||||
@ -2482,6 +2482,11 @@ static bool can_subword_truncate(Node* in, const Type* type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Since casts specifically change the type of a node, stay on the safe side and do not truncate them.
|
||||
if (in->is_ConstraintCast()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cannot be truncated:
|
||||
switch (opc) {
|
||||
case Op_AbsI:
|
||||
|
||||
@ -75,6 +75,7 @@
|
||||
// v.release_store(x) -> void
|
||||
// v.release_store_fence(x) -> void
|
||||
// v.compare_exchange(x, y [, o]) -> T
|
||||
// v.compare_set(x, y [, o]) -> bool
|
||||
// v.exchange(x [, o]) -> T
|
||||
//
|
||||
// (2) All atomic types are default constructible.
|
||||
@ -267,6 +268,11 @@ public:
|
||||
return AtomicAccess::cmpxchg(value_ptr(), compare_value, new_value, order);
|
||||
}
|
||||
|
||||
bool compare_set(T compare_value, T new_value,
|
||||
atomic_memory_order order = memory_order_conservative) {
|
||||
return compare_exchange(compare_value, new_value, order) == compare_value;
|
||||
}
|
||||
|
||||
T exchange(T new_value,
|
||||
atomic_memory_order order = memory_order_conservative) {
|
||||
return AtomicAccess::xchg(this->value_ptr(), new_value, order);
|
||||
@ -479,6 +485,13 @@ public:
|
||||
order));
|
||||
}
|
||||
|
||||
bool compare_set(T compare_value, T new_value,
|
||||
atomic_memory_order order = memory_order_conservative) {
|
||||
return _value.compare_set(decay(compare_value),
|
||||
decay(new_value),
|
||||
order);
|
||||
}
|
||||
|
||||
T exchange(T new_value, atomic_memory_order order = memory_order_conservative) {
|
||||
return recover(_value.exchange(decay(new_value), order));
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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
|
||||
@ -466,10 +466,7 @@ void before_exit(JavaThread* thread, bool halt) {
|
||||
event.commit();
|
||||
}
|
||||
|
||||
// 2nd argument (emit_event_shutdown) should be set to false
|
||||
// because EventShutdown would be emitted at Threads::destroy_vm().
|
||||
// (one of the callers of before_exit())
|
||||
JFR_ONLY(Jfr::on_vm_shutdown(true, false, halt);)
|
||||
JFR_ONLY(Jfr::on_vm_shutdown(false, halt);)
|
||||
|
||||
// Stop the WatcherThread. We do this before disenrolling various
|
||||
// PeriodicTasks to reduce the likelihood of races.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2026, 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,24 @@
|
||||
#ifndef SHARE_RUNTIME_PREFETCH_INLINE_HPP
|
||||
#define SHARE_RUNTIME_PREFETCH_INLINE_HPP
|
||||
|
||||
#include "runtime/prefetch.hpp"
|
||||
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
|
||||
// If calls to prefetch methods are in a loop, the loop should be cloned
|
||||
// such that if Prefetch{Scan,Copy}Interval and/or PrefetchFieldInterval
|
||||
// say not to do prefetching, these methods aren't called. At the very
|
||||
// least, they take up a memory issue slot. They should be implemented
|
||||
// as inline assembly code: doing an actual call isn't worth the cost.
|
||||
|
||||
class Prefetch : AllStatic {
|
||||
public:
|
||||
// Prefetch anticipating read; must not fault, semantically a no-op
|
||||
static void read(const void* loc, intx interval);
|
||||
|
||||
// Prefetch anticipating write; must not fault, semantically a no-op
|
||||
static void write(void* loc, intx interval);
|
||||
};
|
||||
|
||||
#include OS_CPU_HEADER_INLINE(prefetch)
|
||||
|
||||
#endif // SHARE_RUNTIME_PREFETCH_INLINE_HPP
|
||||
|
||||
@ -191,8 +191,7 @@
|
||||
//
|
||||
// C2 stub blob/field names
|
||||
//
|
||||
// C2 stubs are provided with names in the format "C2 Runtime
|
||||
// <stubname> _blob".
|
||||
// C2 stubs are provided with names in the format "<stubname>_blob (C2 runtime)".
|
||||
//
|
||||
// A stub creation method OptoRuntime::generate(ciEnv* env) is
|
||||
// generated which invokes the C2 compiler to generate each stub in
|
||||
|
||||
@ -479,7 +479,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
|
||||
#define PROCESS_SHARED_BLOB(name, type) \
|
||||
process_shared_blob(_group_cursor, _blob_cursor, \
|
||||
_stub_cursor, _entry_cursor, \
|
||||
"Shared Runtime " # name "_blob", \
|
||||
#name "_blob (shared runtime)", \
|
||||
BlobId:: JOIN3(shared, name, id), \
|
||||
StubId:: JOIN3(shared, name, id), \
|
||||
EntryId:: JOIN3(shared, name, id), \
|
||||
@ -488,7 +488,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
|
||||
#define PROCESS_C1_BLOB(name) \
|
||||
process_c1_blob(_group_cursor, _blob_cursor, \
|
||||
_stub_cursor, _entry_cursor, \
|
||||
"C1 Runtime " # name "_blob", \
|
||||
#name "_blob (C1 runtime)", \
|
||||
BlobId:: JOIN3(c1, name, id), \
|
||||
StubId:: JOIN3(c1, name, id), \
|
||||
EntryId:: JOIN3(c1, name, id)); \
|
||||
@ -496,7 +496,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
|
||||
#define PROCESS_C2_BLOB(name, type) \
|
||||
process_c2_blob(_group_cursor, _blob_cursor, \
|
||||
_stub_cursor, _entry_cursor, \
|
||||
"C2 Runtime " # name "_blob", \
|
||||
#name "_blob (C2 runtime)", \
|
||||
BlobId:: JOIN3(c2, name, id), \
|
||||
StubId:: JOIN3(c2, name, id), \
|
||||
EntryId:: JOIN3(c2, name, id)); \
|
||||
@ -504,7 +504,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
|
||||
#define PROCESS_C2_STUB(name, fancy_jump, pass_tls, return_pc) \
|
||||
process_c2_blob(_group_cursor, _blob_cursor, \
|
||||
_stub_cursor, _entry_cursor, \
|
||||
"C2 Runtime " # name "_blob", \
|
||||
#name "_blob (C2 runtime)", \
|
||||
BlobId:: JOIN3(c2, name, id), \
|
||||
StubId:: JOIN3(c2, name, id), \
|
||||
EntryId:: JOIN3(c2, name, id)); \
|
||||
@ -512,20 +512,20 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
|
||||
#define PROCESS_STUBGEN_BLOB(blob) \
|
||||
process_stubgen_blob(_group_cursor, _blob_cursor, \
|
||||
_stub_cursor, _entry_cursor, \
|
||||
"Stub Generator " # blob "_blob", \
|
||||
#blob "_blob (stub gen)", \
|
||||
BlobId:: JOIN3(stubgen, blob, id)); \
|
||||
|
||||
#define PROCESS_STUBGEN_STUB(blob, stub) \
|
||||
process_stubgen_stub(_group_cursor, _blob_cursor, \
|
||||
_stub_cursor, _entry_cursor, \
|
||||
"Stub Generator " # stub "_stub", \
|
||||
#stub "_stub (stub gen)", \
|
||||
BlobId:: JOIN3(stubgen, blob, id), \
|
||||
StubId:: JOIN3(stubgen, stub, id)); \
|
||||
|
||||
#define PROCESS_STUBGEN_ENTRY(blob, stub, field_name, getter_name) \
|
||||
process_stubgen_entry(_group_cursor, _blob_cursor, \
|
||||
_stub_cursor, _entry_cursor, \
|
||||
"Stub Generator " # field_name "_entry", \
|
||||
#field_name "_entry (stub gen)", \
|
||||
BlobId:: JOIN3(stubgen, blob, id), \
|
||||
StubId:: JOIN3(stubgen, stub, id), \
|
||||
EntryId:: JOIN3(stubgen, field_name, id), \
|
||||
@ -535,7 +535,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
|
||||
init_funcion) \
|
||||
process_stubgen_entry(_group_cursor, _blob_cursor, \
|
||||
_stub_cursor, _entry_cursor, \
|
||||
"Stub Generator " # field_name "_entry", \
|
||||
#field_name "_entry (stub gen)", \
|
||||
BlobId:: JOIN3(stubgen, blob, id), \
|
||||
StubId:: JOIN3(stubgen, stub, id), \
|
||||
EntryId:: JOIN3(stubgen, field_name, id), \
|
||||
@ -545,7 +545,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
|
||||
count) \
|
||||
process_stubgen_entry(_group_cursor, _blob_cursor, \
|
||||
_stub_cursor, _entry_cursor, \
|
||||
"Stub Generator " # field_name "_entry", \
|
||||
#field_name "_entry (stub gen)", \
|
||||
BlobId:: JOIN3(stubgen, blob, id), \
|
||||
StubId:: JOIN3(stubgen, stub, id), \
|
||||
EntryId:: JOIN3(stubgen, field_name, id), \
|
||||
@ -567,7 +567,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor,
|
||||
init_function) \
|
||||
process_stubgen_entry(_group_cursor, _blob_cursor, \
|
||||
_stub_cursor, _entry_cursor, \
|
||||
"Stub Generator " # arch_name "_" # field_name "_entry", \
|
||||
#arch_name "_" # field_name "_entry (stub gen)",\
|
||||
BlobId:: JOIN3(stubgen, blob, id), \
|
||||
StubId:: JOIN3(stubgen, stub, id), \
|
||||
EntryId:: JOIN4(stubgen, arch_name, \
|
||||
|
||||
@ -157,7 +157,7 @@ inline bool ConcurrentHashTable<CONFIG, MT>::
|
||||
if (is_locked()) {
|
||||
return false;
|
||||
}
|
||||
if (_first.compare_exchange(expect, node) == expect) {
|
||||
if (_first.compare_set(expect, node)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -172,7 +172,7 @@ inline bool ConcurrentHashTable<CONFIG, MT>::
|
||||
}
|
||||
// We will expect a clean first pointer.
|
||||
Node* tmp = first();
|
||||
if (_first.compare_exchange(tmp, set_state(tmp, STATE_LOCK_BIT)) == tmp) {
|
||||
if (_first.compare_set(tmp, set_state(tmp, STATE_LOCK_BIT))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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
|
||||
@ -63,6 +63,9 @@
|
||||
#include "utilities/nativeStackPrinter.hpp"
|
||||
#include "utilities/unsigned5.hpp"
|
||||
#include "utilities/vmError.hpp"
|
||||
#if INCLUDE_JFR
|
||||
#include "jfr/jfr.hpp"
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@ -262,6 +265,8 @@ void report_untested(const char* file, int line, const char* message) {
|
||||
void report_java_out_of_memory(const char* message) {
|
||||
static int out_of_memory_reported = 0;
|
||||
|
||||
JFR_ONLY(Jfr::on_report_java_out_of_memory();)
|
||||
|
||||
// A number of threads may attempt to report OutOfMemoryError at around the
|
||||
// same time. To avoid dumping the heap or executing the data collection
|
||||
// commands multiple times we just do it once when the first threads reports
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2024 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2023, 2025, Red Hat, Inc. and/or its affiliates.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
@ -1898,7 +1898,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt
|
||||
log.set_fd(-1);
|
||||
}
|
||||
|
||||
JFR_ONLY(Jfr::on_vm_shutdown(static_cast<VMErrorType>(_id) == OOM_JAVA_HEAP_FATAL, true);)
|
||||
JFR_ONLY(Jfr::on_vm_shutdown(true, false, static_cast<VMErrorType>(_id) == OOM_JAVA_HEAP_FATAL);)
|
||||
|
||||
if (PrintNMTStatistics) {
|
||||
fdStream fds(fd_out);
|
||||
|
||||
@ -181,7 +181,7 @@ void GenericWaitBarrier::Cell::disarm(int32_t expected_tag) {
|
||||
tag, waiters);
|
||||
|
||||
int64_t new_state = encode(0, waiters);
|
||||
if (_state.compare_exchange(state, new_state) == state) {
|
||||
if (_state.compare_set(state, new_state)) {
|
||||
// Successfully disarmed.
|
||||
break;
|
||||
}
|
||||
@ -218,7 +218,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) {
|
||||
tag, waiters);
|
||||
|
||||
int64_t new_state = encode(tag, waiters + 1);
|
||||
if (_state.compare_exchange(state, new_state) == state) {
|
||||
if (_state.compare_set(state, new_state)) {
|
||||
// Success! Proceed to wait.
|
||||
break;
|
||||
}
|
||||
@ -247,7 +247,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) {
|
||||
tag, waiters);
|
||||
|
||||
int64_t new_state = encode(tag, waiters - 1);
|
||||
if (_state.compare_exchange(state, new_state) == state) {
|
||||
if (_state.compare_set(state, new_state)) {
|
||||
// Success!
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2045,19 +2045,26 @@ public final class String
|
||||
return encode(Charset.defaultCharset(), coder(), value);
|
||||
}
|
||||
|
||||
boolean bytesCompatible(Charset charset) {
|
||||
boolean bytesCompatible(Charset charset, int srcIndex, int numChars) {
|
||||
if (isLatin1()) {
|
||||
if (charset == ISO_8859_1.INSTANCE) {
|
||||
return true; // ok, same encoding
|
||||
} else if (charset == UTF_8.INSTANCE || charset == US_ASCII.INSTANCE) {
|
||||
return !StringCoding.hasNegatives(value, 0, value.length); // ok, if ASCII-compatible
|
||||
return !StringCoding.hasNegatives(value, srcIndex, numChars); // ok, if ASCII-compatible
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void copyToSegmentRaw(MemorySegment segment, long offset) {
|
||||
MemorySegment.copy(value, 0, segment, ValueLayout.JAVA_BYTE, offset, value.length);
|
||||
void copyToSegmentRaw(MemorySegment segment, long offset, int srcIndex, int srcLength) {
|
||||
if (!isLatin1()) {
|
||||
// This method is intended to be used together with bytesCompatible, which currently only supports
|
||||
// latin1 strings. In the future, bytesCompatible could be updated to handle more cases, like
|
||||
// UTF-16 strings (when the platform and charset endianness match, and the String doesn’t contain
|
||||
// unpaired surrogates). If that happens, copyToSegmentRaw should also be updated.
|
||||
throw new IllegalStateException("This string does not support copyToSegmentRaw");
|
||||
}
|
||||
MemorySegment.copy(value, srcIndex, segment, ValueLayout.JAVA_BYTE, offset, srcLength);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -2331,13 +2331,13 @@ public final class System {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyToSegmentRaw(String string, MemorySegment segment, long offset) {
|
||||
string.copyToSegmentRaw(segment, offset);
|
||||
public void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) {
|
||||
string.copyToSegmentRaw(segment, offset, srcIndex, srcLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bytesCompatible(String string, Charset charset) {
|
||||
return string.bytesCompatible(charset);
|
||||
public boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) {
|
||||
return string.bytesCompatible(charset, srcIndex, numChars);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -31,6 +31,8 @@ import java.lang.classfile.Instruction;
|
||||
import java.lang.classfile.Opcode;
|
||||
|
||||
import jdk.internal.classfile.impl.AbstractInstruction;
|
||||
import jdk.internal.classfile.impl.BytecodeHelpers;
|
||||
import jdk.internal.classfile.impl.Util;
|
||||
|
||||
/**
|
||||
* Models a local variable increment instruction in the {@code code} array of a
|
||||
@ -82,6 +84,37 @@ public sealed interface IncrementInstruction extends Instruction
|
||||
* @throws IllegalArgumentException if {@code slot} or {@code constant} is out of range
|
||||
*/
|
||||
static IncrementInstruction of(int slot, int constant) {
|
||||
return new AbstractInstruction.UnboundIncrementInstruction(slot, constant);
|
||||
var opcode = BytecodeHelpers.validateAndIsWideIinc(slot, constant) ? Opcode.IINC_W: Opcode.IINC;
|
||||
return new AbstractInstruction.UnboundIncrementInstruction(opcode, slot, constant);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return an increment instruction}
|
||||
* <p>
|
||||
* {@code slot} must be {@link java.lang.classfile##u1 u1} and
|
||||
* {@code constant} must be within {@code [-128, 127]} for
|
||||
* {@link Opcode#IINC iinc}, or {@code slot} must be
|
||||
* {@link java.lang.classfile##u2 u2} and {@code constant} must be
|
||||
* within {@code [-32768, 32767]} for {@link Opcode#IINC_W wide iinc}.
|
||||
*
|
||||
* @apiNote
|
||||
* The explicit {@code op} argument allows creating {@code wide} or
|
||||
* regular increment instructions when {@code slot} and
|
||||
* {@code constant} can be encoded with more optimized
|
||||
* increment instructions.
|
||||
*
|
||||
* @param op the opcode for the specific type of increment instruction,
|
||||
* which must be of kind {@link Opcode.Kind#INCREMENT}
|
||||
* @param slot the local variable slot to increment
|
||||
* @param constant the increment constant
|
||||
* @throws IllegalArgumentException if the opcode kind is not
|
||||
* {@link Opcode.Kind#INCREMENT} or {@code slot} or
|
||||
* {@code constant} is out of range
|
||||
* @since 27
|
||||
*/
|
||||
static IncrementInstruction of(Opcode op, int slot, int constant) {
|
||||
Util.checkKind(op, Opcode.Kind.INCREMENT);
|
||||
BytecodeHelpers.validateIncrement(op, slot, constant);
|
||||
return new AbstractInstruction.UnboundIncrementInstruction(op, slot, constant);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1296,12 +1296,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
* over the decoding process is required.
|
||||
* <p>
|
||||
* Getting a string from a segment with a known byte offset and
|
||||
* known byte length can be done like so:
|
||||
* {@snippet lang=java :
|
||||
* byte[] bytes = new byte[length];
|
||||
* MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, length);
|
||||
* return new String(bytes, charset);
|
||||
* }
|
||||
* known byte length can be done using {@link #getString(long, Charset, long)}.
|
||||
*
|
||||
* @param offset offset in bytes (relative to this segment address) at which this
|
||||
* access operation will occur
|
||||
@ -1328,6 +1323,41 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
*/
|
||||
String getString(long offset, Charset charset);
|
||||
|
||||
/**
|
||||
* Reads a string from this segment at the given offset, using the provided length
|
||||
* and charset.
|
||||
* <p>
|
||||
* This method always replaces malformed-input and unmappable-character
|
||||
* sequences with this charset's default replacement string. The {@link
|
||||
* java.nio.charset.CharsetDecoder} class should be used when more control
|
||||
* over the decoding process is required.
|
||||
* <p>
|
||||
* If the string contains any {@code '\0'} characters, they will be read as well.
|
||||
* This differs from {@link #getString(long, Charset)}, which will only read up
|
||||
* to the first {@code '\0'}, resulting in truncation for string data that contains
|
||||
* the {@code '\0'} character.
|
||||
*
|
||||
* @param offset offset in bytes (relative to this segment address) at which this
|
||||
* access operation will occur
|
||||
* @param charset the charset used to {@linkplain Charset#newDecoder() decode} the
|
||||
* string bytes
|
||||
* @param byteLength length, in bytes, of the region of memory to read and decode into
|
||||
* a string
|
||||
* @return a Java string constructed from the bytes read from the given starting
|
||||
* address up to the given length
|
||||
* @throws IllegalArgumentException if the size of the string is greater than the
|
||||
* largest string supported by the platform
|
||||
* @throws IndexOutOfBoundsException if {@code offset < 0}
|
||||
* @throws IndexOutOfBoundsException if {@code offset > byteSize() - byteLength}
|
||||
* @throws IllegalStateException if the {@linkplain #scope() scope} associated with
|
||||
* this segment is not {@linkplain Scope#isAlive() alive}
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code isAccessibleBy(T) == false}
|
||||
* @throws IllegalArgumentException if {@code byteLength < 0}
|
||||
* @since 27
|
||||
*/
|
||||
String getString(long offset, Charset charset, long byteLength);
|
||||
|
||||
/**
|
||||
* Writes the given string into this segment at the given offset, converting it to
|
||||
* a null-terminated byte sequence using the {@linkplain StandardCharsets#UTF_8 UTF-8}
|
||||
@ -1366,7 +1396,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again.
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link #getString(long, Charset, long)}.
|
||||
*
|
||||
* @param offset offset in bytes (relative to this segment address) at which this
|
||||
* access operation will occur, the final address of this write
|
||||
@ -2606,6 +2637,51 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
elementCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the byte sequence of the given string encoded using the provided charset
|
||||
* to the destination segment.
|
||||
* <p>
|
||||
* This method always replaces malformed-input and unmappable-character
|
||||
* sequences with this charset's default replacement string. The {@link
|
||||
* java.nio.charset.CharsetDecoder} class should be used when more control
|
||||
* over the decoding process is required.
|
||||
* <p>
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link #getString(long, Charset, long)}.
|
||||
*
|
||||
* @param src the Java string to be written into the destination segment
|
||||
* @param dstEncoding the charset used to {@linkplain Charset#newEncoder() encode}
|
||||
* the string bytes.
|
||||
* @param srcIndex the starting character index of the source string
|
||||
* @param dst the destination segment
|
||||
* @param dstOffset the starting offset, in bytes, of the destination segment
|
||||
* @param numChars the number of characters to be copied
|
||||
* @throws IllegalStateException if the {@linkplain #scope() scope} associated with
|
||||
* {@code dst} is not {@linkplain Scope#isAlive() alive}
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code dst.isAccessibleBy(T) == false}
|
||||
* @throws IndexOutOfBoundsException if either {@code srcIndex}, {@code numChars}, or {@code dstOffset}
|
||||
* are {@code < 0}
|
||||
* @throws IndexOutOfBoundsException if {@code srcIndex > src.length() - numChars}
|
||||
* @throws IllegalArgumentException if {@code dst} is {@linkplain #isReadOnly() read-only}
|
||||
* @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - B} where {@code B} is the size,
|
||||
* in bytes, of the substring of {@code src} encoded using the given charset
|
||||
* @return the number of copied bytes.
|
||||
* @since 27
|
||||
*/
|
||||
@ForceInline
|
||||
static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) {
|
||||
Objects.requireNonNull(src);
|
||||
Objects.requireNonNull(dstEncoding);
|
||||
Objects.requireNonNull(dst);
|
||||
Objects.checkFromIndexSize(srcIndex, numChars, src.length());
|
||||
|
||||
return AbstractMemorySegmentImpl.copy(src, dstEncoding, srcIndex, dst, dstOffset, numChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the relative offset, in bytes, of the first mismatch between the
|
||||
* source and the destination segments. More specifically, the bytes at offset
|
||||
|
||||
@ -111,7 +111,8 @@ public interface SegmentAllocator {
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again.
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link MemorySegment#getString(long, Charset, long)}.
|
||||
*
|
||||
* @param str the Java string to be converted into a C string
|
||||
* @param charset the charset used to {@linkplain Charset#newEncoder() encode} the
|
||||
@ -137,10 +138,10 @@ public interface SegmentAllocator {
|
||||
int termCharSize = StringSupport.CharsetKind.of(charset).terminatorCharSize();
|
||||
MemorySegment segment;
|
||||
int length;
|
||||
if (StringSupport.bytesCompatible(str, charset)) {
|
||||
if (StringSupport.bytesCompatible(str, charset, 0, str.length())) {
|
||||
length = str.length();
|
||||
segment = allocateNoInit((long) length + termCharSize);
|
||||
StringSupport.copyToSegmentRaw(str, segment, 0);
|
||||
StringSupport.copyToSegmentRaw(str, segment, 0, 0, str.length());
|
||||
} else {
|
||||
byte[] bytes = str.getBytes(charset);
|
||||
length = bytes.length;
|
||||
@ -153,6 +154,54 @@ public interface SegmentAllocator {
|
||||
return segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a Java string using the provided charset and stores the resulting
|
||||
* byte array into a memory segment.
|
||||
* <p>
|
||||
* This method always replaces malformed-input and unmappable-character
|
||||
* sequences with this charset's default replacement byte array. The
|
||||
* {@link java.nio.charset.CharsetEncoder} class should be used when more
|
||||
* control over the encoding process is required.
|
||||
* <p>
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link MemorySegment#getString(long, Charset, long)}.
|
||||
*
|
||||
* @param str the Java string to be encoded
|
||||
* @param charset the charset used to {@linkplain Charset#newEncoder() encode} the
|
||||
* string bytes
|
||||
* @param srcIndex the starting index of the source string
|
||||
* @param numChars the number of characters to be copied
|
||||
* @return a new native segment containing the encoded string
|
||||
* @throws IndexOutOfBoundsException if either {@code srcIndex} or {@code numChars} are {@code < 0}
|
||||
* @throws IndexOutOfBoundsException if {@code srcIndex > str.length() - numChars}
|
||||
*
|
||||
* @implSpec The default implementation for this method copies the contents of the
|
||||
* provided Java string into a new memory segment obtained by calling
|
||||
* {@code this.allocate(B)}, where {@code B} is the size, in bytes, of
|
||||
* the string encoded using the provided charset
|
||||
* (e.g. {@code str.getBytes(charset).length});
|
||||
* @since 27
|
||||
*/
|
||||
@ForceInline
|
||||
default MemorySegment allocateFrom(String str, Charset charset, int srcIndex, int numChars) {
|
||||
Objects.requireNonNull(charset);
|
||||
Objects.requireNonNull(str);
|
||||
Objects.checkFromIndexSize(srcIndex, numChars, str.length());
|
||||
MemorySegment segment;
|
||||
if (StringSupport.bytesCompatible(str, charset, srcIndex, numChars)) {
|
||||
segment = allocateNoInit(numChars);
|
||||
StringSupport.copyToSegmentRaw(str, segment, 0, srcIndex, numChars);
|
||||
} else {
|
||||
byte[] bytes = str.substring(srcIndex, srcIndex + numChars).getBytes(charset);
|
||||
segment = allocateNoInit(bytes.length);
|
||||
MemorySegment.copy(bytes, 0, segment, ValueLayout.JAVA_BYTE, 0, bytes.length);
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new memory segment initialized with the provided byte value}
|
||||
* <p>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2026, 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,11 @@ public class GZIPInputStream extends InflaterInputStream {
|
||||
super(in, createInflater(in, size), size);
|
||||
usesDefaultInflater = true;
|
||||
try {
|
||||
readHeader(in);
|
||||
// we don't expect the stream to be at EOF
|
||||
// and if it is, then we want readHeader to
|
||||
// raise an exception, so we pass "true" for
|
||||
// the "failOnEOF" param.
|
||||
readHeader(in, true);
|
||||
} catch (IOException ioe) {
|
||||
this.inf.end();
|
||||
throw ioe;
|
||||
@ -190,12 +194,40 @@ public class GZIPInputStream extends InflaterInputStream {
|
||||
/*
|
||||
* Reads GZIP member header and returns the total byte number
|
||||
* of this member header.
|
||||
* If failOnEOF is false and if the given InputStream has already
|
||||
* reached EOF when this method was invoked, then this method returns
|
||||
* -1 (indicating that there's no GZIP member header).
|
||||
* In all other cases of malformed header or EOF being detected
|
||||
* when reading the header, this method will throw an IOException.
|
||||
*/
|
||||
private int readHeader(InputStream this_in) throws IOException {
|
||||
private int readHeader(InputStream this_in, boolean failOnEOF) throws IOException {
|
||||
CheckedInputStream in = new CheckedInputStream(this_in, crc);
|
||||
crc.reset();
|
||||
|
||||
int magic;
|
||||
if (!failOnEOF) {
|
||||
// read an unsigned short value representing the GZIP magic header.
|
||||
// this is the same as calling readUShort(in), except that here,
|
||||
// when reading the first byte, we don't raise an EOFException
|
||||
// if the stream has already reached EOF.
|
||||
|
||||
// read unsigned byte
|
||||
int b = in.read();
|
||||
if (b == -1) { // EOF
|
||||
crc.reset();
|
||||
return -1; // represents no header bytes available
|
||||
}
|
||||
checkUnexpectedByte(b);
|
||||
// read the next unsigned byte to form the unsigned
|
||||
// short. we throw the usual EOFException/ZipException
|
||||
// from this point on if there is no more data or
|
||||
// the data doesn't represent a header.
|
||||
magic = (readUByte(in) << 8) | b;
|
||||
} else {
|
||||
magic = readUShort(in);
|
||||
}
|
||||
// Check header magic
|
||||
if (readUShort(in) != GZIP_MAGIC) {
|
||||
if (magic != GZIP_MAGIC) {
|
||||
throw new ZipException("Not in GZIP format");
|
||||
}
|
||||
// Check compression method
|
||||
@ -261,7 +293,11 @@ public class GZIPInputStream extends InflaterInputStream {
|
||||
// try concatenated case
|
||||
int m = 8; // this.trailer
|
||||
try {
|
||||
m += readHeader(in); // next.header
|
||||
int numNextHeaderBytes = readHeader(in, false); // next.header (if available)
|
||||
if (numNextHeaderBytes == -1) {
|
||||
return true; // end of stream reached
|
||||
}
|
||||
m += numNextHeaderBytes;
|
||||
} catch (IOException ze) {
|
||||
return true; // ignore any malformed, do nothing
|
||||
}
|
||||
@ -295,14 +331,18 @@ public class GZIPInputStream extends InflaterInputStream {
|
||||
if (b == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
if (b < -1 || b > 255) {
|
||||
// Report on this.in, not argument in; see read{Header, Trailer}.
|
||||
throw new IOException(this.in.getClass().getName()
|
||||
+ ".read() returned value out of range -1..255: " + b);
|
||||
}
|
||||
checkUnexpectedByte(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
private void checkUnexpectedByte(final int b) throws IOException {
|
||||
if (b < -1 || b > 255) {
|
||||
// report the InputStream type which returned this unexpected byte
|
||||
throw new IOException(this.in.getClass().getName()
|
||||
+ ".read() returned value out of range -1..255: " + b);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] tmpbuf = new byte[128];
|
||||
|
||||
/*
|
||||
|
||||
@ -634,10 +634,10 @@ public interface JavaLangAccess {
|
||||
/**
|
||||
* Copy the string bytes to an existing segment, avoiding intermediate copies.
|
||||
*/
|
||||
void copyToSegmentRaw(String string, MemorySegment segment, long offset);
|
||||
void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength);
|
||||
|
||||
/**
|
||||
* Are the string bytes compatible with the given charset?
|
||||
*/
|
||||
boolean bytesCompatible(String string, Charset charset);
|
||||
boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars);
|
||||
}
|
||||
|
||||
@ -832,10 +832,8 @@ public abstract sealed class AbstractInstruction
|
||||
final int slot;
|
||||
final int constant;
|
||||
|
||||
public UnboundIncrementInstruction(int slot, int constant) {
|
||||
super(BytecodeHelpers.validateAndIsWideIinc(slot, constant)
|
||||
? Opcode.IINC_W
|
||||
: Opcode.IINC);
|
||||
public UnboundIncrementInstruction(Opcode op, int slot, int constant) {
|
||||
super(op);
|
||||
this.slot = slot;
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
@ -450,6 +450,13 @@ public class BytecodeHelpers {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void validateIncrement(Opcode opcode, int slot, int constant) {
|
||||
if (validateAndIsWideIinc(slot, constant) && opcode != Opcode.IINC_W) {
|
||||
throw new IllegalArgumentException(
|
||||
"IINC: operands require wide encoding for %s".formatted(opcode));
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateRet(Opcode opcode, int slot) {
|
||||
if (opcode == Opcode.RET && (slot & ~0xFF) == 0 ||
|
||||
opcode == Opcode.RET_W && (slot & ~0xFFFF) == 0)
|
||||
|
||||
@ -551,6 +551,13 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
unsafeGetOffset() == that.unsafeGetOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(long offset, Charset charset, long byteLength) {
|
||||
Utils.checkNonNegativeArgument(byteLength, "byteLength");
|
||||
Objects.requireNonNull(charset);
|
||||
return StringSupport.read(this, offset, charset, byteLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
@ -702,6 +709,16 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) {
|
||||
Objects.requireNonNull(src);
|
||||
Objects.requireNonNull(dstEncoding);
|
||||
Objects.requireNonNull(dst);
|
||||
|
||||
AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dst;
|
||||
return StringSupport.copyBytes(src, destImpl, dstEncoding, dstOffset, srcIndex, numChars);
|
||||
}
|
||||
|
||||
// accessors
|
||||
|
||||
@ForceInline
|
||||
|
||||
@ -30,11 +30,14 @@ import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.ScopedMemoryAccess;
|
||||
import jdk.internal.util.Architecture;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
import jdk.internal.util.Preconditions;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.*;
|
||||
|
||||
@ -58,6 +61,27 @@ public final class StringSupport {
|
||||
};
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static String read(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) {
|
||||
return readBytes(segment, offset, charset, length);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static String readBytes(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) {
|
||||
if (length > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Required length exceeds implementation limit");
|
||||
}
|
||||
final int lengthBytes = (int) length;
|
||||
final byte[] bytes = new byte[lengthBytes];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, lengthBytes);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static void write(AbstractMemorySegmentImpl segment, long offset, Charset charset, String string) {
|
||||
switch (CharsetKind.of(charset)) {
|
||||
@ -70,14 +94,7 @@ public final class StringSupport {
|
||||
@ForceInline
|
||||
private static String readByte(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
|
||||
final int len = strlenByte(segment, offset, segment.byteSize());
|
||||
final byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
return readBytes(segment, offset, charset, len);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@ -89,14 +106,7 @@ public final class StringSupport {
|
||||
@ForceInline
|
||||
private static String readShort(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
|
||||
int len = strlenShort(segment, offset, segment.byteSize());
|
||||
byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
return readBytes(segment, offset, charset, len);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@ -108,14 +118,7 @@ public final class StringSupport {
|
||||
@ForceInline
|
||||
private static String readInt(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
|
||||
int len = strlenInt(segment, offset, segment.byteSize());
|
||||
byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
return readBytes(segment, offset, charset, len);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@ -345,22 +348,26 @@ public final class StringSupport {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean bytesCompatible(String string, Charset charset) {
|
||||
return JAVA_LANG_ACCESS.bytesCompatible(string, charset);
|
||||
public static boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) {
|
||||
return JAVA_LANG_ACCESS.bytesCompatible(string, charset, srcIndex, numChars);
|
||||
}
|
||||
|
||||
public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset) {
|
||||
if (bytesCompatible(string, charset)) {
|
||||
copyToSegmentRaw(string, segment, offset);
|
||||
return string.length();
|
||||
return copyBytes(string, segment, charset, offset, 0, string.length());
|
||||
}
|
||||
|
||||
public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset, int srcIndex, int numChars) {
|
||||
if (bytesCompatible(string, charset, srcIndex, numChars)) {
|
||||
copyToSegmentRaw(string, segment, offset, srcIndex, numChars);
|
||||
return numChars;
|
||||
} else {
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
byte[] bytes = string.substring(srcIndex, srcIndex + numChars).getBytes(charset);
|
||||
MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length);
|
||||
return bytes.length;
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyToSegmentRaw(String string, MemorySegment segment, long offset) {
|
||||
JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset);
|
||||
public static void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) {
|
||||
JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset, srcIndex, srcLength);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -68,7 +68,7 @@ public class ECDSAOperations {
|
||||
|
||||
public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) {
|
||||
this.ecOps = ecOps;
|
||||
this.basePoint = toAffinePoint(basePoint, ecOps.getField());
|
||||
this.basePoint = AffinePoint.fromECPoint(basePoint, ecOps.getField());
|
||||
}
|
||||
|
||||
public ECOperations getEcOperations() {
|
||||
@ -79,14 +79,6 @@ public class ECDSAOperations {
|
||||
return ecOps.multiply(basePoint, scalar).asAffine();
|
||||
}
|
||||
|
||||
public static AffinePoint toAffinePoint(ECPoint point,
|
||||
IntegerFieldModuloP field) {
|
||||
|
||||
ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX());
|
||||
ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY());
|
||||
return new AffinePoint(affineX, affineY);
|
||||
}
|
||||
|
||||
public static
|
||||
Optional<ECDSAOperations> forParameters(ECParameterSpec ecParams) {
|
||||
Optional<ECOperations> curveOps =
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, 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,8 @@ import javax.lang.model.type.DeclaredType;
|
||||
* method. There is no guarantee that any particular annotation will
|
||||
* always be represented by the same object.
|
||||
*
|
||||
* @jls 9.6 Annotation Interfaces
|
||||
* @jls 9.7 Annotations
|
||||
* @since 1.6
|
||||
*/
|
||||
public interface AnnotationMirror {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, 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,14 +27,16 @@ package javax.lang.model.element;
|
||||
|
||||
/**
|
||||
* Represents a value of an annotation interface element.
|
||||
* A value is of one of the following types:
|
||||
* <ul><li> a wrapper class (such as {@link Integer}) for a primitive type
|
||||
* <li> {@code String}
|
||||
* <li> {@code TypeMirror}
|
||||
* <li> {@code VariableElement} (representing an enum constant)
|
||||
* <li> {@code AnnotationMirror}
|
||||
* A value is of one of the following types (JLS {@jls 9.6.1}):
|
||||
* <ul><li> a {@linkplain java.lang##wrapperClass wrapper class} to hold a
|
||||
* primitive type, such as an {@code Integer} object to hold an
|
||||
* {@code int}
|
||||
* <li> {@code String} representing a {@code String}
|
||||
* <li> {@link javax.lang.model.type.TypeMirror TypeMirror} representing a {@code Class} literal
|
||||
* <li> {@link VariableElement} representing an enum constant
|
||||
* <li> {@link AnnotationMirror} representing an annotation
|
||||
* <li> {@code List<? extends AnnotationValue>}
|
||||
* (representing the elements, in declared order, if the value is an array)
|
||||
* representing the elements, in declared order, if the value is an array
|
||||
* </ul>
|
||||
*
|
||||
* @since 1.6
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, 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
|
||||
@ -120,6 +120,15 @@ public interface TypeMirror extends AnnotatedConstruct {
|
||||
* The results of {@code t1.equals(t2)} and
|
||||
* {@code Types.isSameType(t1, t2)} may differ.
|
||||
*
|
||||
* @apiNote The identity of a {@code TypeMirror} involves implicit
|
||||
* state not directly accessible from its methods, including state
|
||||
* about the presence of unrelated types. {@code TypeMirror}
|
||||
* objects created by different implementations of these
|
||||
* interfaces should <i>not</i> be expected to compare as equal
|
||||
* even if "the same" type is being modeled; this is
|
||||
* analogous to the inequality of {@code Class} objects for the
|
||||
* same class file loaded through different class loaders.
|
||||
*
|
||||
* @param obj the object to be compared with this type
|
||||
* @return {@code true} if the specified object is equal to this one
|
||||
*/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, 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
|
||||
@ -106,6 +106,15 @@ public interface Types {
|
||||
* {@code TypeMirror} objects can have different annotations and
|
||||
* still be considered the same.
|
||||
*
|
||||
* @apiNote The identity of a {@code TypeMirror} involves implicit
|
||||
* state not directly accessible from its methods, including state
|
||||
* about the presence of unrelated types. {@code TypeMirror}
|
||||
* objects created by different implementations of these
|
||||
* interfaces should <i>not</i> be expected to compare as equal
|
||||
* even if "the same" type is being modeled; this is
|
||||
* analogous to the inequality of {@code Class} objects for the
|
||||
* same class file loaded through different class loaders.
|
||||
*
|
||||
* @param t1 the first type
|
||||
* @param t2 the second type
|
||||
* @return {@code true} if and only if the two types are the same
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2026, 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
|
||||
@ -162,8 +162,7 @@ void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* cre
|
||||
sampleRate, // sample rate
|
||||
DAUDIO_PCM, // only accept PCM
|
||||
bits == 8 ? FALSE : TRUE, // signed
|
||||
bits == 8 ? FALSE // little-endian for 8bit
|
||||
: UTIL_IsBigEndianPlatform());
|
||||
FALSE); // all supported macOS versions run on LE
|
||||
}
|
||||
}
|
||||
// add default format
|
||||
@ -175,7 +174,7 @@ void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* cre
|
||||
defSampleRate, // sample rate
|
||||
DAUDIO_PCM, // PCM
|
||||
TRUE, // signed
|
||||
UTIL_IsBigEndianPlatform()); // native endianness
|
||||
FALSE); // native endianness; all supported macOS versions run on LE
|
||||
}
|
||||
|
||||
TRACE0("<<DAUDIO_GetFormats\n");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2026, 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 com.sun.media.sound;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
@ -40,12 +41,6 @@ final class Platform {
|
||||
|
||||
private static boolean isNativeLibLoaded;
|
||||
|
||||
// SYSTEM CHARACTERISTICS
|
||||
// vary according to hardware architecture
|
||||
|
||||
// intel is little-endian. sparc is big-endian.
|
||||
private static boolean bigEndian;
|
||||
|
||||
static {
|
||||
loadLibraries();
|
||||
}
|
||||
@ -66,7 +61,7 @@ final class Platform {
|
||||
* Determine whether the system is big-endian.
|
||||
*/
|
||||
static boolean isBigEndian() {
|
||||
return bigEndian;
|
||||
return ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,9 +77,6 @@ final class Platform {
|
||||
if (Printer.err) Printer.err("Couldn't load library "+libName+": "+t.toString());
|
||||
isNativeLibLoaded = false;
|
||||
}
|
||||
if (isNativeLibLoaded) {
|
||||
bigEndian = nIsBigEndian();
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isMidiIOEnabled() {
|
||||
@ -98,7 +90,4 @@ final class Platform {
|
||||
static boolean isDirectAudioEnabled() {
|
||||
return isNativeLibLoaded;
|
||||
}
|
||||
|
||||
// the following native method is implemented in Platform.c
|
||||
private static native boolean nIsBigEndian();
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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
|
||||
@ -1405,7 +1405,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
|
||||
// If the splitpane has a zero size then no op out of here.
|
||||
// If we execute this function now, we're going to cause ourselves
|
||||
// much grief.
|
||||
if (containerSize.height <= 0 || containerSize.width <= 0 ) {
|
||||
if (containerSize.height <= 0 && containerSize.width <= 0 ) {
|
||||
lastSplitPaneSize = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2026, 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
|
||||
@ -302,8 +302,16 @@ public class PNGImageDecoder extends ImageDecoder
|
||||
int bitsPerPixel = samplesPerPixel*bitDepth;
|
||||
int bytesPerPixel = (bitsPerPixel+7)>>3;
|
||||
int pass, passLimit;
|
||||
if(interlaceMethod==0) { pass = -1; passLimit = 0; }
|
||||
else { pass = 0; passLimit = 7; }
|
||||
boolean isDirectByteCopy;
|
||||
if(interlaceMethod==0) {
|
||||
pass = -1;
|
||||
passLimit = 0;
|
||||
isDirectByteCopy = bPixels != null && bitDepth == 8;
|
||||
} else {
|
||||
pass = 0;
|
||||
passLimit = 7;
|
||||
isDirectByteCopy = false;
|
||||
}
|
||||
while(++pass<=passLimit) {
|
||||
int row = startingRow[pass];
|
||||
int rowInc = rowIncrement[pass];
|
||||
@ -334,7 +342,11 @@ public class PNGImageDecoder extends ImageDecoder
|
||||
int spos=0;
|
||||
int pixel = 0;
|
||||
while (col < width) {
|
||||
if(wPixels !=null) {
|
||||
if (isDirectByteCopy) {
|
||||
System.arraycopy(rowByteBuffer, spos, bPixels, col + rowOffset, width);
|
||||
spos += width;
|
||||
break;
|
||||
} else if(wPixels !=null) {
|
||||
switch(combinedType) {
|
||||
case COLOR|ALPHA|(8<<3):
|
||||
wPixels[col+rowOffset] =
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2026, 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
|
||||
@ -92,8 +92,8 @@ public class XbmImageDecoder extends ImageDecoder {
|
||||
byte[] raster = null;
|
||||
IndexColorModel model = null;
|
||||
|
||||
String matchRegex = "(0[xX])?[0-9a-fA-F]+[\\s+]?[,|};]";
|
||||
String replaceRegex = "(0[xX])|,|[\\s+]|[};]";
|
||||
String matchRegex = "\\s*(0[xX])?((?:(?!,|\\};).)+)(,|\\};)";
|
||||
String replaceRegex = "0[xX]|,|\\s+|\\};";
|
||||
|
||||
String line;
|
||||
int lineNum = 0;
|
||||
@ -111,11 +111,19 @@ public class XbmImageDecoder extends ImageDecoder {
|
||||
}
|
||||
try {
|
||||
if (!token[2].isBlank() && state == 0) {
|
||||
W = Integer.parseInt(token[2]);
|
||||
state = 1; // after width is set
|
||||
if (token[1].endsWith("th")) {
|
||||
W = Integer.parseInt(token[2]);
|
||||
} else if (token[1].endsWith("t")) {
|
||||
H = Integer.parseInt(token[2]);
|
||||
}
|
||||
state = 1; // after first dimension is set
|
||||
} else if (!token[2].isBlank() && state == 1) {
|
||||
H = Integer.parseInt(token[2]);
|
||||
state = 2; // after height is set
|
||||
if (token[1].endsWith("th")) {
|
||||
W = Integer.parseInt(token[2]);
|
||||
} else if (token[1].endsWith("t")) {
|
||||
H = Integer.parseInt(token[2]);
|
||||
}
|
||||
state = 2; // after second dimension is set
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
// parseInt() can throw NFE
|
||||
@ -147,59 +155,81 @@ public class XbmImageDecoder extends ImageDecoder {
|
||||
error("Width or Height of XBM file not defined");
|
||||
}
|
||||
|
||||
boolean contFlag = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// loop to process image data
|
||||
while (!aborted && (line = br.readLine()) != null) {
|
||||
lineNum++;
|
||||
|
||||
if (line.contains("[]")) {
|
||||
Matcher matcher = Pattern.compile(matchRegex).matcher(line);
|
||||
while (matcher.find()) {
|
||||
if (y >= H) {
|
||||
error("Scan size of XBM file exceeds"
|
||||
+ " the defined width x height");
|
||||
}
|
||||
|
||||
int startIndex = matcher.start();
|
||||
int endIndex = matcher.end();
|
||||
String hexByte = line.substring(startIndex, endIndex);
|
||||
|
||||
if (!(hexByte.startsWith("0x")
|
||||
|| hexByte.startsWith("0X"))) {
|
||||
error("Invalid hexadecimal number at Ln#:" + lineNum
|
||||
+ " Col#:" + (startIndex + 1));
|
||||
}
|
||||
hexByte = hexByte.replaceAll(replaceRegex, "");
|
||||
if (hexByte.length() != 2) {
|
||||
error("Invalid hexadecimal number at Ln#:" + lineNum
|
||||
+ " Col#:" + (startIndex + 1));
|
||||
}
|
||||
|
||||
try {
|
||||
n = Integer.parseInt(hexByte, 16);
|
||||
} catch (NumberFormatException nfe) {
|
||||
error("Error parsing hexadecimal at Ln#:" + lineNum
|
||||
+ " Col#:" + (startIndex + 1));
|
||||
}
|
||||
for (int mask = 1; mask <= 0x80; mask <<= 1) {
|
||||
if (x < W) {
|
||||
if ((n & mask) != 0)
|
||||
raster[x] = 1;
|
||||
else
|
||||
raster[x] = 0;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
|
||||
if (x >= W) {
|
||||
int result = setPixels(0, y, W, 1, model, raster, 0, W);
|
||||
if (result <= 0) {
|
||||
error("Unexpected error occurred during setPixel()");
|
||||
}
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
if (!contFlag) {
|
||||
if (line.contains("[]")) {
|
||||
contFlag = true;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int end = line.indexOf(';');
|
||||
if (end >= 0) {
|
||||
sb.append(line, 0, end + 1);
|
||||
break;
|
||||
} else {
|
||||
sb.append(line).append(System.lineSeparator());
|
||||
}
|
||||
}
|
||||
|
||||
String resultLine = sb.toString();
|
||||
int cutOffIndex = resultLine.indexOf('{');
|
||||
resultLine = resultLine.substring(cutOffIndex + 1);
|
||||
|
||||
Matcher matcher = Pattern.compile(matchRegex).matcher(resultLine);
|
||||
while (matcher.find()) {
|
||||
if (y >= H) {
|
||||
error("Scan size of XBM file exceeds"
|
||||
+ " the defined width x height");
|
||||
}
|
||||
|
||||
int startIndex = matcher.start();
|
||||
int endIndex = matcher.end();
|
||||
String hexByte = resultLine.substring(startIndex, endIndex);
|
||||
hexByte = hexByte.replaceAll("^\\s+", "");
|
||||
|
||||
if (!(hexByte.startsWith("0x")
|
||||
|| hexByte.startsWith("0X"))) {
|
||||
error("Invalid hexadecimal number at Ln#:" + lineNum
|
||||
+ " Col#:" + (startIndex + 1));
|
||||
}
|
||||
hexByte = hexByte.replaceAll(replaceRegex, "");
|
||||
if (hexByte.length() != 2) {
|
||||
error("Invalid hexadecimal number at Ln#:" + lineNum
|
||||
+ " Col#:" + (startIndex + 1));
|
||||
}
|
||||
|
||||
try {
|
||||
n = Integer.parseInt(hexByte, 16);
|
||||
} catch (NumberFormatException nfe) {
|
||||
error("Error parsing hexadecimal at Ln#:" + lineNum
|
||||
+ " Col#:" + (startIndex + 1));
|
||||
}
|
||||
for (int mask = 1; mask <= 0x80; mask <<= 1) {
|
||||
if (x < W) {
|
||||
if ((n & mask) != 0)
|
||||
raster[x] = 1;
|
||||
else
|
||||
raster[x] = 0;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
|
||||
if (x >= W) {
|
||||
int result = setPixels(0, y, W, 1, model, raster, 0, W);
|
||||
if (result <= 0) {
|
||||
error("Unexpected error occurred during setPixel()");
|
||||
}
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
imageComplete(ImageConsumer.STATICIMAGEDONE, true);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, 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,15 +26,6 @@
|
||||
|
||||
#include "Utilities.h"
|
||||
|
||||
|
||||
int UTIL_IsBigEndianPlatform() {
|
||||
#ifdef _LITTLE_ENDIAN
|
||||
return 0;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ThrowJavaMessageException(JNIEnv *e, const char *exClass, const char *msg) {
|
||||
jclass newExcCls;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, 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,10 +28,6 @@
|
||||
#include "SoundDefs.h"
|
||||
#include "Configure.h" // put flags for debug msgs etc. here
|
||||
|
||||
// return 1 if this platform is big endian, or 0 for little endian
|
||||
int UTIL_IsBigEndianPlatform();
|
||||
|
||||
|
||||
// ERROR PRINTS
|
||||
#ifdef USE_ERROR
|
||||
#define ERROR0(string) { fprintf(stdout, (string)); fflush(stdout); }
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2009, 2026, 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
|
||||
@ -1403,6 +1403,7 @@ public class TypeAnnotations {
|
||||
break;
|
||||
}
|
||||
}
|
||||
scan(tree.dims);
|
||||
scan(tree.elems);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, 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
|
||||
@ -1107,6 +1107,7 @@ public class Annotate {
|
||||
for (List<JCAnnotation> dimAnnos : tree.dimAnnotations)
|
||||
enterTypeAnnotations(dimAnnos, env, sym, false);
|
||||
scan(tree.elemtype);
|
||||
scan(tree.dims);
|
||||
scan(tree.elems);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, 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,6 +50,7 @@ import jdk.jpackage.internal.model.LinuxLauncher;
|
||||
import jdk.jpackage.internal.model.LinuxPackage;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.util.CompositeProxy;
|
||||
import jdk.jpackage.internal.util.Enquoter;
|
||||
import jdk.jpackage.internal.util.PathUtils;
|
||||
import jdk.jpackage.internal.util.XmlUtils;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, 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,13 +27,12 @@ package jdk.jpackage.internal;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -48,9 +47,6 @@ public final class LibProvidersLookup {
|
||||
return (new ToolValidator(TOOL_LDD).validate() == null);
|
||||
}
|
||||
|
||||
public LibProvidersLookup() {
|
||||
}
|
||||
|
||||
LibProvidersLookup setPackageLookup(PackageLookup v) {
|
||||
packageLookup = v;
|
||||
return this;
|
||||
@ -87,23 +83,20 @@ public final class LibProvidersLookup {
|
||||
}
|
||||
|
||||
private static List<Path> getNeededLibsForFile(Path path) throws IOException {
|
||||
List<Path> result = new ArrayList<>();
|
||||
int ret = Executor.of(TOOL_LDD, path.toString()).setOutputConsumer(lines -> {
|
||||
lines.map(line -> {
|
||||
Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null;
|
||||
}).filter(Objects::nonNull).map(Path::of).forEach(result::add);
|
||||
}).execute();
|
||||
final var result = Executor.of(TOOL_LDD, path.toString()).saveOutput().execute();
|
||||
|
||||
if (ret != 0) {
|
||||
if (result.getExitCode() != 0) {
|
||||
// objdump failed. This is OK if the tool was applied to not a binary file
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return result;
|
||||
return result.stdout().stream().map(line -> {
|
||||
Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null;
|
||||
}).filter(Objects::nonNull).map(Path::of).toList();
|
||||
}
|
||||
|
||||
private static Collection<Path> getNeededLibsForFiles(List<Path> paths) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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 static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_R
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.cli.Options;
|
||||
import jdk.jpackage.internal.cli.StandardBundlingOperation;
|
||||
@ -44,19 +45,34 @@ import jdk.jpackage.internal.util.Result;
|
||||
public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
|
||||
public LinuxBundlingEnvironment() {
|
||||
super(build()
|
||||
.defaultOperation(() -> {
|
||||
return LazyLoad.SYS_ENV.value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get);
|
||||
})
|
||||
.bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage)
|
||||
.bundler(CREATE_LINUX_DEB, LazyLoad::debSysEnv, LinuxBundlingEnvironment::createDebPackage)
|
||||
.bundler(CREATE_LINUX_RPM, LazyLoad::rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage));
|
||||
super(build().mutate(builder -> {
|
||||
|
||||
// Wrap the generic Linux system environment supplier in the run-once wrapper
|
||||
// as this supplier is called from both RPM and DEB Linux system environment suppliers.
|
||||
var sysEnv = runOnce(() -> {
|
||||
return LinuxSystemEnvironment.create();
|
||||
});
|
||||
|
||||
Supplier<Result<LinuxDebSystemEnvironment>> debSysEnv = () -> {
|
||||
return LinuxDebSystemEnvironment.create(sysEnv.get());
|
||||
};
|
||||
|
||||
Supplier<Result<LinuxRpmSystemEnvironment>> rpmSysEnv = () -> {
|
||||
return LinuxRpmSystemEnvironment.create(sysEnv.get());
|
||||
};
|
||||
|
||||
builder.defaultOperation(() -> {
|
||||
return sysEnv.get().value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get);
|
||||
})
|
||||
.bundler(CREATE_LINUX_DEB, debSysEnv, LinuxBundlingEnvironment::createDebPackage)
|
||||
.bundler(CREATE_LINUX_RPM, rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage);
|
||||
}).bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage));
|
||||
}
|
||||
|
||||
private static void createDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) {
|
||||
|
||||
createNativePackage(options,
|
||||
LinuxFromOptions.createLinuxDebPackage(options),
|
||||
LinuxFromOptions.createLinuxDebPackage(options, sysEnv),
|
||||
buildEnv()::create,
|
||||
LinuxBundlingEnvironment::buildPipeline,
|
||||
(env, pkg, outputDir) -> {
|
||||
@ -67,7 +83,7 @@ public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
private static void createRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) {
|
||||
|
||||
createNativePackage(options,
|
||||
LinuxFromOptions.createLinuxRpmPackage(options),
|
||||
LinuxFromOptions.createLinuxRpmPackage(options, sysEnv),
|
||||
buildEnv()::create,
|
||||
LinuxBundlingEnvironment::buildPipeline,
|
||||
(env, pkg, outputDir) -> {
|
||||
@ -90,23 +106,6 @@ public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment {
|
||||
return new BuildEnvFromOptions().predefinedAppImageLayout(APPLICATION_LAYOUT);
|
||||
}
|
||||
|
||||
private static final class LazyLoad {
|
||||
|
||||
static Result<LinuxDebSystemEnvironment> debSysEnv() {
|
||||
return DEB_SYS_ENV;
|
||||
}
|
||||
|
||||
static Result<LinuxRpmSystemEnvironment> rpmSysEnv() {
|
||||
return RPM_SYS_ENV;
|
||||
}
|
||||
|
||||
private static final Result<LinuxSystemEnvironment> SYS_ENV = LinuxSystemEnvironment.create();
|
||||
|
||||
private static final Result<LinuxDebSystemEnvironment> DEB_SYS_ENV = LinuxDebSystemEnvironment.create(SYS_ENV);
|
||||
|
||||
private static final Result<LinuxRpmSystemEnvironment> RPM_SYS_ENV = LinuxRpmSystemEnvironment.create(SYS_ENV);
|
||||
}
|
||||
|
||||
private static final Map<PackageType, BundlingOperationDescriptor> DESCRIPTORS = Stream.of(
|
||||
CREATE_LINUX_DEB,
|
||||
CREATE_LINUX_RPM
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026, 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,6 @@
|
||||
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -76,11 +75,11 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
|
||||
try {
|
||||
// Try the real path first as it works better on newer Ubuntu versions
|
||||
return findProvidingPackages(realPath, sysEnv.dpkg());
|
||||
return findProvidingPackages(realPath, sysEnv);
|
||||
} catch (IOException ex) {
|
||||
// Try the default path if differ
|
||||
if (!realPath.equals(file)) {
|
||||
return findProvidingPackages(file, sysEnv.dpkg());
|
||||
return findProvidingPackages(file, sysEnv);
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
@ -107,7 +106,7 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
|
||||
properties.forEach(property -> cmdline.add(property.name));
|
||||
|
||||
Map<String, String> actualValues = Executor.of(cmdline.toArray(String[]::new))
|
||||
Map<String, String> actualValues = Executor.of(cmdline)
|
||||
.saveOutput(true)
|
||||
.executeExpectSuccess()
|
||||
.getOutput().stream()
|
||||
@ -158,9 +157,8 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
cmdline.addAll(List.of("-b", env.appImageDir().toString(), debFile.toAbsolutePath().toString()));
|
||||
|
||||
// run dpkg
|
||||
RetryExecutor.retryOnKnownErrorMessage(
|
||||
"semop(1): encountered an error: Invalid argument").execute(
|
||||
cmdline.toArray(String[]::new));
|
||||
Executor.of(cmdline).retryOnKnownErrorMessage(
|
||||
"semop(1): encountered an error: Invalid argument").execute();
|
||||
|
||||
Log.verbose(I18N.format("message.output-to-location", debFile.toAbsolutePath()));
|
||||
}
|
||||
@ -233,7 +231,7 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<String> findProvidingPackages(Path file, Path dpkg) throws IOException {
|
||||
private static Stream<String> findProvidingPackages(Path file, LinuxDebSystemEnvironment sysEnv) throws IOException {
|
||||
//
|
||||
// `dpkg -S` command does glob pattern lookup. If not the absolute path
|
||||
// to the file is specified it might return mltiple package names.
|
||||
@ -279,9 +277,9 @@ final class LinuxDebPackager extends LinuxPackager<LinuxDebPackage> {
|
||||
Set<String> archPackages = new HashSet<>();
|
||||
Set<String> otherPackages = new HashSet<>();
|
||||
|
||||
var debArch = LinuxPackageArch.getValue(LINUX_DEB);
|
||||
var debArch = sysEnv.packageArch().value();
|
||||
|
||||
Executor.of(dpkg.toString(), "-S", file.toString())
|
||||
Executor.of(sysEnv.dpkg().toString(), "-S", file.toString())
|
||||
.saveOutput(true).executeExpectSuccess()
|
||||
.getOutput().forEach(line -> {
|
||||
Matcher matcher = PACKAGE_NAME_REGEX.matcher(line);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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,7 +28,7 @@ import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin;
|
||||
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin {
|
||||
interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin {
|
||||
|
||||
static Result<LinuxDebSystemEnvironment> create(Result<LinuxSystemEnvironment> base) {
|
||||
return mixin(LinuxDebSystemEnvironment.class, base, LinuxDebSystemEnvironmentMixin::create);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,7 +29,7 @@ import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxDebSystemEnvironmentMixin {
|
||||
interface LinuxDebSystemEnvironmentMixin {
|
||||
Path dpkg();
|
||||
Path dpkgdeb();
|
||||
Path fakeroot();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
@ -70,9 +70,9 @@ final class LinuxFromOptions {
|
||||
return LinuxApplication.create(appBuilder.create());
|
||||
}
|
||||
|
||||
static LinuxRpmPackage createLinuxRpmPackage(Options options) {
|
||||
static LinuxRpmPackage createLinuxRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) {
|
||||
|
||||
final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_RPM);
|
||||
final var superPkgBuilder = createLinuxPackageBuilder(options, sysEnv, LINUX_RPM);
|
||||
|
||||
final var pkgBuilder = new LinuxRpmPackageBuilder(superPkgBuilder);
|
||||
|
||||
@ -81,9 +81,9 @@ final class LinuxFromOptions {
|
||||
return pkgBuilder.create();
|
||||
}
|
||||
|
||||
static LinuxDebPackage createLinuxDebPackage(Options options) {
|
||||
static LinuxDebPackage createLinuxDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) {
|
||||
|
||||
final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_DEB);
|
||||
final var superPkgBuilder = createLinuxPackageBuilder(options, sysEnv, LINUX_DEB);
|
||||
|
||||
final var pkgBuilder = new LinuxDebPackageBuilder(superPkgBuilder);
|
||||
|
||||
@ -99,7 +99,7 @@ final class LinuxFromOptions {
|
||||
return pkg;
|
||||
}
|
||||
|
||||
private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, StandardPackageType type) {
|
||||
private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, LinuxSystemEnvironment sysEnv, StandardPackageType type) {
|
||||
|
||||
final var app = createLinuxApplication(options);
|
||||
|
||||
@ -107,6 +107,8 @@ final class LinuxFromOptions {
|
||||
|
||||
final var pkgBuilder = new LinuxPackageBuilder(superPkgBuilder);
|
||||
|
||||
pkgBuilder.arch(sysEnv.packageArch());
|
||||
|
||||
LINUX_PACKAGE_DEPENDENCIES.ifPresentIn(options, pkgBuilder::additionalDependencies);
|
||||
LINUX_APP_CATEGORY.ifPresentIn(options, pkgBuilder::category);
|
||||
LINUX_MENU_GROUP.ifPresentIn(options, pkgBuilder::menuGroupName);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2026, 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
|
||||
@ -32,6 +32,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import jdk.jpackage.internal.model.Launcher;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.util.Enquoter;
|
||||
|
||||
/**
|
||||
* Helper to install launchers as services using "systemd".
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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,18 +25,20 @@
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier;
|
||||
import java.util.ArrayList;
|
||||
import jdk.jpackage.internal.model.StandardPackageType;
|
||||
import jdk.jpackage.internal.util.CommandOutputControl;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
final class LinuxPackageArch {
|
||||
record LinuxPackageArch(String value) {
|
||||
|
||||
static String getValue(StandardPackageType pkgType) {
|
||||
static Result<LinuxPackageArch> create(StandardPackageType pkgType) {
|
||||
switch (pkgType) {
|
||||
case LINUX_RPM -> {
|
||||
return RpmPackageArch.VALUE;
|
||||
return rpm().map(LinuxPackageArch::new);
|
||||
}
|
||||
case LINUX_DEB -> {
|
||||
return DebPackageArch.VALUE;
|
||||
return deb().map(LinuxPackageArch::new);
|
||||
}
|
||||
default -> {
|
||||
throw new IllegalArgumentException();
|
||||
@ -44,62 +46,51 @@ final class LinuxPackageArch {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DebPackageArch {
|
||||
|
||||
static final String VALUE = toSupplier(DebPackageArch::getValue).get();
|
||||
|
||||
private static String getValue() throws IOException {
|
||||
return Executor.of("dpkg", "--print-architecture").saveOutput(true)
|
||||
.executeExpectSuccess().getOutput().get(0);
|
||||
}
|
||||
private static Result<String> deb() {
|
||||
var exec = Executor.of("dpkg", "--print-architecture").saveOutput(true);
|
||||
return Result.of(exec::executeExpectSuccess, IOException.class)
|
||||
.flatMap(LinuxPackageArch::getStdoutFirstLine);
|
||||
}
|
||||
|
||||
private static class RpmPackageArch {
|
||||
|
||||
/*
|
||||
* Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is mandatory for
|
||||
* rpm packaging, try it first. rpm is optional and may not be available, use as the last
|
||||
* resort.
|
||||
*/
|
||||
private static enum RpmArchReader {
|
||||
Rpmbuild("rpmbuild", "--eval=%{_target_cpu}"),
|
||||
Rpm("rpm", "--eval=%{_target_cpu}");
|
||||
|
||||
RpmArchReader(String... cmdline) {
|
||||
this.cmdline = cmdline;
|
||||
private static Result<String> rpm() {
|
||||
var errors = new ArrayList<Exception>();
|
||||
for (var tool : RpmArchReader.values()) {
|
||||
var result = tool.getRpmArch();
|
||||
if (result.hasValue()) {
|
||||
return result;
|
||||
} else {
|
||||
errors.addAll(result.errors());
|
||||
}
|
||||
|
||||
String getRpmArch() throws IOException {
|
||||
Executor exec = Executor.of(cmdline).saveOutput(true);
|
||||
switch (this) {
|
||||
case Rpm -> {
|
||||
exec.executeExpectSuccess();
|
||||
}
|
||||
case Rpmbuild -> {
|
||||
if (exec.execute() != 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
return exec.getOutput().get(0);
|
||||
}
|
||||
|
||||
private final String[] cmdline;
|
||||
}
|
||||
|
||||
static final String VALUE = toSupplier(RpmPackageArch::getValue).get();
|
||||
return Result.ofErrors(errors);
|
||||
}
|
||||
|
||||
private static String getValue() throws IOException {
|
||||
for (var rpmArchReader : RpmArchReader.values()) {
|
||||
var rpmArchStr = rpmArchReader.getRpmArch();
|
||||
if (rpmArchStr != null) {
|
||||
return rpmArchStr;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("error.rpm-arch-not-detected");
|
||||
/*
|
||||
* Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is mandatory for
|
||||
* rpm packaging, try it first. rpm is optional and may not be available, use as the last
|
||||
* resort.
|
||||
*/
|
||||
private enum RpmArchReader {
|
||||
RPMBUILD("rpmbuild", "--eval=%{_target_cpu}"),
|
||||
RPM("rpm", "--eval=%{_target_cpu}");
|
||||
|
||||
RpmArchReader(String... cmdline) {
|
||||
this.cmdline = cmdline;
|
||||
}
|
||||
|
||||
Result<String> getRpmArch() {
|
||||
var exec = Executor.of(cmdline).saveOutput(true);
|
||||
return Result.of(exec::executeExpectSuccess, IOException.class)
|
||||
.flatMap(LinuxPackageArch::getStdoutFirstLine);
|
||||
}
|
||||
|
||||
private final String[] cmdline;
|
||||
}
|
||||
|
||||
private static Result<String> getStdoutFirstLine(CommandOutputControl.Result result) {
|
||||
return Result.of(() -> {
|
||||
return result.stdout().stream().findFirst().orElseThrow(result::unexpected);
|
||||
}, IOException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
@ -83,7 +83,7 @@ final class LinuxPackageBuilder {
|
||||
category(),
|
||||
Optional.ofNullable(additionalDependencies),
|
||||
release(),
|
||||
pkg.asStandardPackageType().map(LinuxPackageArch::getValue).orElseThrow()));
|
||||
arch.value()));
|
||||
}
|
||||
|
||||
LinuxPackageBuilder literalName(String v) {
|
||||
@ -119,6 +119,11 @@ final class LinuxPackageBuilder {
|
||||
return Optional.ofNullable(release);
|
||||
}
|
||||
|
||||
LinuxPackageBuilder arch(LinuxPackageArch v) {
|
||||
arch = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
private static LinuxApplicationLayout usrTreePackageLayout(Path prefix, String packageName) {
|
||||
final var lib = prefix.resolve(Path.of("lib", packageName));
|
||||
return LinuxApplicationLayout.create(
|
||||
@ -184,6 +189,7 @@ final class LinuxPackageBuilder {
|
||||
private String category;
|
||||
private String additionalDependencies;
|
||||
private String release;
|
||||
private LinuxPackageArch arch;
|
||||
|
||||
private final PackageBuilder pkgBuilder;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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,7 +28,7 @@ import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin;
|
||||
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin {
|
||||
interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin {
|
||||
|
||||
static Result<LinuxRpmSystemEnvironment> create(Result<LinuxSystemEnvironment> base) {
|
||||
return mixin(LinuxRpmSystemEnvironment.class, base, LinuxRpmSystemEnvironmentMixin::create);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
@ -32,7 +32,7 @@ import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.model.DottedVersion;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxRpmSystemEnvironmentMixin {
|
||||
interface LinuxRpmSystemEnvironmentMixin {
|
||||
Path rpm();
|
||||
Path rpmbuild();
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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 @@ package jdk.jpackage.internal;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import jdk.jpackage.internal.model.PackageType;
|
||||
@ -35,9 +34,10 @@ import jdk.jpackage.internal.model.StandardPackageType;
|
||||
import jdk.jpackage.internal.util.CompositeProxy;
|
||||
import jdk.jpackage.internal.util.Result;
|
||||
|
||||
public interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
boolean soLookupAvailable();
|
||||
PackageType nativePackageType();
|
||||
LinuxPackageArch packageArch();
|
||||
|
||||
static Result<LinuxSystemEnvironment> create() {
|
||||
return detectNativePackageType().map(LinuxSystemEnvironment::create).orElseGet(() -> {
|
||||
@ -45,7 +45,7 @@ public interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
});
|
||||
}
|
||||
|
||||
static Optional<PackageType> detectNativePackageType() {
|
||||
static Optional<StandardPackageType> detectNativePackageType() {
|
||||
if (Internal.isDebian()) {
|
||||
return Optional.of(StandardPackageType.LINUX_DEB);
|
||||
} else if (Internal.isRpm()) {
|
||||
@ -55,13 +55,14 @@ public interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
}
|
||||
}
|
||||
|
||||
static Result<LinuxSystemEnvironment> create(PackageType nativePackageType) {
|
||||
return Result.ofValue(new Stub(LibProvidersLookup.supported(),
|
||||
Objects.requireNonNull(nativePackageType)));
|
||||
static Result<LinuxSystemEnvironment> create(StandardPackageType nativePackageType) {
|
||||
return LinuxPackageArch.create(nativePackageType).map(arch -> {
|
||||
return new Stub(LibProvidersLookup.supported(), nativePackageType, arch);
|
||||
});
|
||||
}
|
||||
|
||||
static <T, U extends LinuxSystemEnvironment> U createWithMixin(Class<U> type, LinuxSystemEnvironment base, T mixin) {
|
||||
return CompositeProxy.create(type, base, mixin);
|
||||
return CompositeProxy.build().invokeTunnel(CompositeProxyTunnel.INSTANCE).create(type, base, mixin);
|
||||
}
|
||||
|
||||
static <T, U extends LinuxSystemEnvironment> Result<U> mixin(Class<U> type,
|
||||
@ -79,7 +80,7 @@ public interface LinuxSystemEnvironment extends SystemEnvironment {
|
||||
}
|
||||
}
|
||||
|
||||
record Stub(boolean soLookupAvailable, PackageType nativePackageType) implements LinuxSystemEnvironment {
|
||||
record Stub(boolean soLookupAvailable, PackageType nativePackageType, LinuxPackageArch packageArch) implements LinuxSystemEnvironment {
|
||||
}
|
||||
|
||||
static final class Internal {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user