mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-03 03:30:05 +00:00
Merge branch 'openjdk:master' into JDK-8373118
This commit is contained in:
commit
f2842dcd29
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 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
|
||||
@ -279,13 +279,11 @@ typename GenericTaskQueue<E, MT, N>::PopResult GenericTaskQueue<E, MT, N>::pop_g
|
||||
// Increment top; if it wraps, also increment tag, to distinguish it
|
||||
// from any recent _age for the same top() index.
|
||||
idx_t new_top = increment_index(oldAge.top());
|
||||
// Don't use bottom, since a pop_local might have decremented it.
|
||||
assert_not_underflow(localBot, new_top);
|
||||
idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0);
|
||||
Age newAge(new_top, new_tag);
|
||||
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 result ? PopResult::Success : PopResult::Contended;
|
||||
}
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
#include "utilities/copy.hpp"
|
||||
|
||||
size_t ThreadLocalAllocBuffer::_max_size = 0;
|
||||
unsigned int ThreadLocalAllocBuffer::_target_refills = 0;
|
||||
unsigned int ThreadLocalAllocBuffer::_target_num_refills = 0;
|
||||
|
||||
ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() :
|
||||
_start(nullptr),
|
||||
@ -48,10 +48,10 @@ ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() :
|
||||
_desired_size(0),
|
||||
_refill_waste_limit(0),
|
||||
_allocated_before_last_gc(0),
|
||||
_number_of_refills(0),
|
||||
_num_refills(0),
|
||||
_refill_waste(0),
|
||||
_gc_waste(0),
|
||||
_slow_allocations(0),
|
||||
_num_slow_allocations(0),
|
||||
_allocated_size(0),
|
||||
_allocation_fraction(TLABAllocationWeight) {
|
||||
|
||||
@ -81,7 +81,7 @@ void ThreadLocalAllocBuffer::accumulate_and_reset_statistics(ThreadLocalAllocSta
|
||||
|
||||
print_stats("gc");
|
||||
|
||||
if (_number_of_refills > 0) {
|
||||
if (_num_refills > 0) {
|
||||
// Update allocation history if a reasonable amount of eden was allocated.
|
||||
bool update_allocation_history = used > 0.5 * capacity;
|
||||
|
||||
@ -98,16 +98,16 @@ void ThreadLocalAllocBuffer::accumulate_and_reset_statistics(ThreadLocalAllocSta
|
||||
_allocation_fraction.sample(alloc_frac);
|
||||
}
|
||||
|
||||
stats->update_fast_allocations(_number_of_refills,
|
||||
stats->update_fast_allocations(_num_refills,
|
||||
_allocated_size,
|
||||
_gc_waste,
|
||||
_refill_waste);
|
||||
} else {
|
||||
assert(_number_of_refills == 0 && _refill_waste == 0 && _gc_waste == 0,
|
||||
assert(_num_refills == 0 && _refill_waste == 0 && _gc_waste == 0,
|
||||
"tlab stats == 0");
|
||||
}
|
||||
|
||||
stats->update_slow_allocations(_slow_allocations);
|
||||
stats->update_num_slow_allocations(_num_slow_allocations);
|
||||
|
||||
reset_statistics();
|
||||
}
|
||||
@ -147,7 +147,7 @@ void ThreadLocalAllocBuffer::resize() {
|
||||
assert(ResizeTLAB, "Should not call this otherwise");
|
||||
size_t alloc = (size_t)(_allocation_fraction.average() *
|
||||
(Universe::heap()->tlab_capacity() / HeapWordSize));
|
||||
size_t new_size = alloc / _target_refills;
|
||||
size_t new_size = alloc / _target_num_refills;
|
||||
|
||||
new_size = clamp(new_size, min_size(), max_size());
|
||||
|
||||
@ -156,24 +156,24 @@ void ThreadLocalAllocBuffer::resize() {
|
||||
log_trace(gc, tlab)("TLAB new size: thread: " PTR_FORMAT " [id: %2d]"
|
||||
" refills %d alloc: %8.6f desired_size: %zu -> %zu",
|
||||
p2i(thread()), thread()->osthread()->thread_id(),
|
||||
_target_refills, _allocation_fraction.average(), desired_size(), aligned_new_size);
|
||||
_target_num_refills, _allocation_fraction.average(), desired_size(), aligned_new_size);
|
||||
|
||||
set_desired_size(aligned_new_size);
|
||||
set_refill_waste_limit(initial_refill_waste_limit());
|
||||
}
|
||||
|
||||
void ThreadLocalAllocBuffer::reset_statistics() {
|
||||
_number_of_refills = 0;
|
||||
_refill_waste = 0;
|
||||
_gc_waste = 0;
|
||||
_slow_allocations = 0;
|
||||
_allocated_size = 0;
|
||||
_num_refills = 0;
|
||||
_refill_waste = 0;
|
||||
_gc_waste = 0;
|
||||
_num_slow_allocations = 0;
|
||||
_allocated_size = 0;
|
||||
}
|
||||
|
||||
void ThreadLocalAllocBuffer::fill(HeapWord* start,
|
||||
HeapWord* top,
|
||||
size_t new_size) {
|
||||
_number_of_refills++;
|
||||
_num_refills++;
|
||||
_allocated_size += new_size;
|
||||
print_stats("fill");
|
||||
assert(top <= start + new_size - alignment_reserve(), "size too small");
|
||||
@ -205,7 +205,7 @@ void ThreadLocalAllocBuffer::initialize() {
|
||||
size_t capacity = Universe::heap()->tlab_capacity() / HeapWordSize;
|
||||
if (capacity > 0) {
|
||||
// Keep alloc_frac as float and not double to avoid the double to float conversion
|
||||
float alloc_frac = desired_size() * target_refills() / (float)capacity;
|
||||
float alloc_frac = desired_size() * target_num_refills() / (float)capacity;
|
||||
_allocation_fraction.sample(alloc_frac);
|
||||
}
|
||||
|
||||
@ -219,10 +219,10 @@ void ThreadLocalAllocBuffer::startup_initialization() {
|
||||
|
||||
// Assuming each thread's active tlab is, on average,
|
||||
// 1/2 full at a GC
|
||||
_target_refills = 100 / (2 * TLABWasteTargetPercent);
|
||||
// We need to set initial target refills to 2 to avoid a GC which causes VM
|
||||
_target_num_refills = 100 / (2 * TLABWasteTargetPercent);
|
||||
// We need to set the initial target number of refills to 2 to avoid a GC which causes VM
|
||||
// abort during VM initialization.
|
||||
_target_refills = MAX2(_target_refills, 2U);
|
||||
_target_num_refills = MAX2(_target_num_refills, 2U);
|
||||
|
||||
// During jvm startup, the main thread is initialized
|
||||
// before the heap is initialized. So reinitialize it now.
|
||||
@ -240,10 +240,10 @@ size_t ThreadLocalAllocBuffer::initial_desired_size() {
|
||||
init_sz = TLABSize / HeapWordSize;
|
||||
} else {
|
||||
// Initial size is a function of the average number of allocating threads.
|
||||
unsigned int nof_threads = ThreadLocalAllocStats::allocating_threads_avg();
|
||||
unsigned int num_threads = ThreadLocalAllocStats::num_allocating_threads_avg();
|
||||
|
||||
init_sz = (Universe::heap()->tlab_capacity() / HeapWordSize) /
|
||||
(nof_threads * target_refills());
|
||||
(num_threads * target_num_refills());
|
||||
init_sz = align_object_size(init_sz);
|
||||
}
|
||||
// We can't use clamp() between min_size() and max_size() here because some
|
||||
@ -271,10 +271,10 @@ void ThreadLocalAllocBuffer::print_stats(const char* tag) {
|
||||
" slow: %dB",
|
||||
tag, p2i(thrd), thrd->osthread()->thread_id(),
|
||||
_desired_size / (K / HeapWordSize),
|
||||
_slow_allocations, _refill_waste_limit * HeapWordSize,
|
||||
_num_slow_allocations, _refill_waste_limit * HeapWordSize,
|
||||
_allocation_fraction.average(),
|
||||
_allocation_fraction.average() * tlab_used / K,
|
||||
_number_of_refills, waste_percent,
|
||||
_num_refills, waste_percent,
|
||||
_gc_waste * HeapWordSize,
|
||||
_refill_waste * HeapWordSize);
|
||||
}
|
||||
@ -299,17 +299,17 @@ HeapWord* ThreadLocalAllocBuffer::hard_end() {
|
||||
return _allocation_end + alignment_reserve();
|
||||
}
|
||||
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_allocating_threads;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_total_refills;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_max_refills;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_num_allocating_threads;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_total_num_refills;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_max_num_refills;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_total_allocated_size;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_total_gc_waste;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_max_gc_waste;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_total_refill_waste;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_max_refill_waste;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_total_slow_allocations;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_max_slow_allocations;
|
||||
AdaptiveWeightedAverage ThreadLocalAllocStats::_allocating_threads_avg(0);
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_total_num_slow_allocations;
|
||||
PerfVariable* ThreadLocalAllocStats::_perf_max_num_slow_allocations;
|
||||
AdaptiveWeightedAverage ThreadLocalAllocStats::_num_allocating_threads_avg(0);
|
||||
|
||||
static PerfVariable* create_perf_variable(const char* name, PerfData::Units unit, TRAPS) {
|
||||
ResourceMark rm;
|
||||
@ -317,47 +317,47 @@ static PerfVariable* create_perf_variable(const char* name, PerfData::Units unit
|
||||
}
|
||||
|
||||
void ThreadLocalAllocStats::initialize() {
|
||||
_allocating_threads_avg = AdaptiveWeightedAverage(TLABAllocationWeight);
|
||||
_allocating_threads_avg.sample(1); // One allocating thread at startup
|
||||
_num_allocating_threads_avg = AdaptiveWeightedAverage(TLABAllocationWeight);
|
||||
_num_allocating_threads_avg.sample(1); // One allocating thread at startup
|
||||
|
||||
if (UsePerfData) {
|
||||
EXCEPTION_MARK;
|
||||
_perf_allocating_threads = create_perf_variable("allocThreads", PerfData::U_None, CHECK);
|
||||
_perf_total_refills = create_perf_variable("fills", PerfData::U_None, CHECK);
|
||||
_perf_max_refills = create_perf_variable("maxFills", PerfData::U_None, CHECK);
|
||||
_perf_total_allocated_size = create_perf_variable("alloc", PerfData::U_Bytes, CHECK);
|
||||
_perf_total_gc_waste = create_perf_variable("gcWaste", PerfData::U_Bytes, CHECK);
|
||||
_perf_max_gc_waste = create_perf_variable("maxGcWaste", PerfData::U_Bytes, CHECK);
|
||||
_perf_total_refill_waste = create_perf_variable("refillWaste", PerfData::U_Bytes, CHECK);
|
||||
_perf_max_refill_waste = create_perf_variable("maxRefillWaste", PerfData::U_Bytes, CHECK);
|
||||
_perf_total_slow_allocations = create_perf_variable("slowAlloc", PerfData::U_None, CHECK);
|
||||
_perf_max_slow_allocations = create_perf_variable("maxSlowAlloc", PerfData::U_None, CHECK);
|
||||
_perf_num_allocating_threads = create_perf_variable("allocThreads", PerfData::U_None, CHECK);
|
||||
_perf_total_num_refills = create_perf_variable("fills", PerfData::U_None, CHECK);
|
||||
_perf_max_num_refills = create_perf_variable("maxFills", PerfData::U_None, CHECK);
|
||||
_perf_total_allocated_size = create_perf_variable("alloc", PerfData::U_Bytes, CHECK);
|
||||
_perf_total_gc_waste = create_perf_variable("gcWaste", PerfData::U_Bytes, CHECK);
|
||||
_perf_max_gc_waste = create_perf_variable("maxGcWaste", PerfData::U_Bytes, CHECK);
|
||||
_perf_total_refill_waste = create_perf_variable("refillWaste", PerfData::U_Bytes, CHECK);
|
||||
_perf_max_refill_waste = create_perf_variable("maxRefillWaste", PerfData::U_Bytes, CHECK);
|
||||
_perf_total_num_slow_allocations = create_perf_variable("slowAlloc", PerfData::U_None, CHECK);
|
||||
_perf_max_num_slow_allocations = create_perf_variable("maxSlowAlloc", PerfData::U_None, CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadLocalAllocStats::ThreadLocalAllocStats() :
|
||||
_allocating_threads(0),
|
||||
_total_refills(0),
|
||||
_max_refills(0),
|
||||
_num_allocating_threads(0),
|
||||
_total_num_refills(0),
|
||||
_max_num_refills(0),
|
||||
_total_allocated_size(0),
|
||||
_total_gc_waste(0),
|
||||
_max_gc_waste(0),
|
||||
_total_refill_waste(0),
|
||||
_max_refill_waste(0),
|
||||
_total_slow_allocations(0),
|
||||
_max_slow_allocations(0) {}
|
||||
_total_num_slow_allocations(0),
|
||||
_max_num_slow_allocations(0) {}
|
||||
|
||||
unsigned int ThreadLocalAllocStats::allocating_threads_avg() {
|
||||
return MAX2((unsigned int)(_allocating_threads_avg.average() + 0.5), 1U);
|
||||
unsigned int ThreadLocalAllocStats::num_allocating_threads_avg() {
|
||||
return MAX2((unsigned int)(_num_allocating_threads_avg.average() + 0.5), 1U);
|
||||
}
|
||||
|
||||
void ThreadLocalAllocStats::update_fast_allocations(unsigned int refills,
|
||||
void ThreadLocalAllocStats::update_fast_allocations(unsigned int num_refills,
|
||||
size_t allocated_size,
|
||||
size_t gc_waste,
|
||||
size_t refill_waste) {
|
||||
_allocating_threads += 1;
|
||||
_total_refills += refills;
|
||||
_max_refills = MAX2(_max_refills, refills);
|
||||
_num_allocating_threads += 1;
|
||||
_total_num_refills += num_refills;
|
||||
_max_num_refills = MAX2(_max_num_refills, num_refills);
|
||||
_total_allocated_size += allocated_size;
|
||||
_total_gc_waste += gc_waste;
|
||||
_max_gc_waste = MAX2(_max_gc_waste, gc_waste);
|
||||
@ -365,35 +365,35 @@ void ThreadLocalAllocStats::update_fast_allocations(unsigned int refills,
|
||||
_max_refill_waste = MAX2(_max_refill_waste, refill_waste);
|
||||
}
|
||||
|
||||
void ThreadLocalAllocStats::update_slow_allocations(unsigned int allocations) {
|
||||
_total_slow_allocations += allocations;
|
||||
_max_slow_allocations = MAX2(_max_slow_allocations, allocations);
|
||||
void ThreadLocalAllocStats::update_num_slow_allocations(unsigned int num_slow_allocations) {
|
||||
_total_num_slow_allocations += num_slow_allocations;
|
||||
_max_num_slow_allocations = MAX2(_max_num_slow_allocations, num_slow_allocations);
|
||||
}
|
||||
|
||||
void ThreadLocalAllocStats::update(const ThreadLocalAllocStats& other) {
|
||||
_allocating_threads += other._allocating_threads;
|
||||
_total_refills += other._total_refills;
|
||||
_max_refills = MAX2(_max_refills, other._max_refills);
|
||||
_total_allocated_size += other._total_allocated_size;
|
||||
_total_gc_waste += other._total_gc_waste;
|
||||
_max_gc_waste = MAX2(_max_gc_waste, other._max_gc_waste);
|
||||
_total_refill_waste += other._total_refill_waste;
|
||||
_max_refill_waste = MAX2(_max_refill_waste, other._max_refill_waste);
|
||||
_total_slow_allocations += other._total_slow_allocations;
|
||||
_max_slow_allocations = MAX2(_max_slow_allocations, other._max_slow_allocations);
|
||||
_num_allocating_threads += other._num_allocating_threads;
|
||||
_total_num_refills += other._total_num_refills;
|
||||
_max_num_refills = MAX2(_max_num_refills, other._max_num_refills);
|
||||
_total_allocated_size += other._total_allocated_size;
|
||||
_total_gc_waste += other._total_gc_waste;
|
||||
_max_gc_waste = MAX2(_max_gc_waste, other._max_gc_waste);
|
||||
_total_refill_waste += other._total_refill_waste;
|
||||
_max_refill_waste = MAX2(_max_refill_waste, other._max_refill_waste);
|
||||
_total_num_slow_allocations += other._total_num_slow_allocations;
|
||||
_max_num_slow_allocations = MAX2(_max_num_slow_allocations, other._max_num_slow_allocations);
|
||||
}
|
||||
|
||||
void ThreadLocalAllocStats::reset() {
|
||||
_allocating_threads = 0;
|
||||
_total_refills = 0;
|
||||
_max_refills = 0;
|
||||
_total_allocated_size = 0;
|
||||
_total_gc_waste = 0;
|
||||
_max_gc_waste = 0;
|
||||
_total_refill_waste = 0;
|
||||
_max_refill_waste = 0;
|
||||
_total_slow_allocations = 0;
|
||||
_max_slow_allocations = 0;
|
||||
_num_allocating_threads = 0;
|
||||
_total_num_refills = 0;
|
||||
_max_num_refills = 0;
|
||||
_total_allocated_size = 0;
|
||||
_total_gc_waste = 0;
|
||||
_max_gc_waste = 0;
|
||||
_total_refill_waste = 0;
|
||||
_max_refill_waste = 0;
|
||||
_total_num_slow_allocations = 0;
|
||||
_max_num_slow_allocations = 0;
|
||||
}
|
||||
|
||||
void ThreadLocalAllocStats::publish() {
|
||||
@ -401,7 +401,7 @@ void ThreadLocalAllocStats::publish() {
|
||||
return;
|
||||
}
|
||||
|
||||
_allocating_threads_avg.sample(_allocating_threads);
|
||||
_num_allocating_threads_avg.sample(_num_allocating_threads);
|
||||
|
||||
const size_t waste = _total_gc_waste + _total_refill_waste;
|
||||
const double waste_percent = percent_of(waste, _total_allocated_size);
|
||||
@ -409,22 +409,22 @@ void ThreadLocalAllocStats::publish() {
|
||||
" slow allocs: %d max %d waste: %4.1f%%"
|
||||
" gc: %zuB max: %zuB"
|
||||
" slow: %zuB max: %zuB",
|
||||
_allocating_threads, _total_refills, _max_refills,
|
||||
_total_slow_allocations, _max_slow_allocations, waste_percent,
|
||||
_num_allocating_threads, _total_num_refills, _max_num_refills,
|
||||
_total_num_slow_allocations, _max_num_slow_allocations, waste_percent,
|
||||
_total_gc_waste * HeapWordSize, _max_gc_waste * HeapWordSize,
|
||||
_total_refill_waste * HeapWordSize, _max_refill_waste * HeapWordSize);
|
||||
|
||||
if (UsePerfData) {
|
||||
_perf_allocating_threads ->set_value(_allocating_threads);
|
||||
_perf_total_refills ->set_value(_total_refills);
|
||||
_perf_max_refills ->set_value(_max_refills);
|
||||
_perf_total_allocated_size ->set_value(_total_allocated_size);
|
||||
_perf_total_gc_waste ->set_value(_total_gc_waste);
|
||||
_perf_max_gc_waste ->set_value(_max_gc_waste);
|
||||
_perf_total_refill_waste ->set_value(_total_refill_waste);
|
||||
_perf_max_refill_waste ->set_value(_max_refill_waste);
|
||||
_perf_total_slow_allocations ->set_value(_total_slow_allocations);
|
||||
_perf_max_slow_allocations ->set_value(_max_slow_allocations);
|
||||
_perf_num_allocating_threads ->set_value(_num_allocating_threads);
|
||||
_perf_total_num_refills ->set_value(_total_num_refills);
|
||||
_perf_max_num_refills ->set_value(_max_num_refills);
|
||||
_perf_total_allocated_size ->set_value(_total_allocated_size);
|
||||
_perf_total_gc_waste ->set_value(_total_gc_waste);
|
||||
_perf_max_gc_waste ->set_value(_max_gc_waste);
|
||||
_perf_total_refill_waste ->set_value(_total_refill_waste);
|
||||
_perf_max_refill_waste ->set_value(_max_refill_waste);
|
||||
_perf_total_num_slow_allocations ->set_value(_total_num_slow_allocations);
|
||||
_perf_max_num_slow_allocations ->set_value(_max_num_slow_allocations);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -56,13 +56,13 @@ private:
|
||||
size_t _refill_waste_limit; // hold onto tlab if free() is larger than this
|
||||
uint64_t _allocated_before_last_gc; // total bytes allocated up until the last gc
|
||||
|
||||
static size_t _max_size; // maximum size of any TLAB
|
||||
static unsigned _target_refills; // expected number of refills between GCs
|
||||
static size_t _max_size; // maximum size of any TLAB
|
||||
static unsigned _target_num_refills; // expected number of refills between GCs
|
||||
|
||||
unsigned _number_of_refills;
|
||||
unsigned _num_refills;
|
||||
unsigned _refill_waste;
|
||||
unsigned _gc_waste;
|
||||
unsigned _slow_allocations;
|
||||
unsigned _num_slow_allocations;
|
||||
size_t _allocated_size;
|
||||
|
||||
AdaptiveWeightedAverage _allocation_fraction; // fraction of eden allocated in tlabs
|
||||
@ -79,7 +79,7 @@ private:
|
||||
|
||||
size_t initial_refill_waste_limit();
|
||||
|
||||
static int target_refills() { return _target_refills; }
|
||||
static int target_num_refills() { return _target_num_refills; }
|
||||
size_t initial_desired_size();
|
||||
|
||||
size_t remaining();
|
||||
@ -98,9 +98,9 @@ private:
|
||||
|
||||
// statistics
|
||||
|
||||
int number_of_refills() const { return _number_of_refills; }
|
||||
int gc_waste() const { return _gc_waste; }
|
||||
int slow_allocations() const { return _slow_allocations; }
|
||||
int num_refills() const { return _num_refills; }
|
||||
int gc_waste() const { return _gc_waste; }
|
||||
int num_slow_allocations() const { return _num_slow_allocations; }
|
||||
|
||||
public:
|
||||
ThreadLocalAllocBuffer();
|
||||
@ -179,41 +179,41 @@ public:
|
||||
|
||||
class ThreadLocalAllocStats : public StackObj {
|
||||
private:
|
||||
static PerfVariable* _perf_allocating_threads;
|
||||
static PerfVariable* _perf_total_refills;
|
||||
static PerfVariable* _perf_max_refills;
|
||||
static PerfVariable* _perf_num_allocating_threads;
|
||||
static PerfVariable* _perf_total_num_refills;
|
||||
static PerfVariable* _perf_max_num_refills;
|
||||
static PerfVariable* _perf_total_allocated_size;
|
||||
static PerfVariable* _perf_total_gc_waste;
|
||||
static PerfVariable* _perf_max_gc_waste;
|
||||
static PerfVariable* _perf_total_refill_waste;
|
||||
static PerfVariable* _perf_max_refill_waste;
|
||||
static PerfVariable* _perf_total_slow_allocations;
|
||||
static PerfVariable* _perf_max_slow_allocations;
|
||||
static PerfVariable* _perf_total_num_slow_allocations;
|
||||
static PerfVariable* _perf_max_num_slow_allocations;
|
||||
|
||||
static AdaptiveWeightedAverage _allocating_threads_avg;
|
||||
static AdaptiveWeightedAverage _num_allocating_threads_avg;
|
||||
|
||||
unsigned int _allocating_threads;
|
||||
unsigned int _total_refills;
|
||||
unsigned int _max_refills;
|
||||
unsigned int _num_allocating_threads;
|
||||
unsigned int _total_num_refills;
|
||||
unsigned int _max_num_refills;
|
||||
size_t _total_allocated_size;
|
||||
size_t _total_gc_waste;
|
||||
size_t _max_gc_waste;
|
||||
size_t _total_refill_waste;
|
||||
size_t _max_refill_waste;
|
||||
unsigned int _total_slow_allocations;
|
||||
unsigned int _max_slow_allocations;
|
||||
unsigned int _total_num_slow_allocations;
|
||||
unsigned int _max_num_slow_allocations;
|
||||
|
||||
public:
|
||||
static void initialize();
|
||||
static unsigned int allocating_threads_avg();
|
||||
static unsigned int num_allocating_threads_avg();
|
||||
|
||||
ThreadLocalAllocStats();
|
||||
|
||||
void update_fast_allocations(unsigned int refills,
|
||||
void update_fast_allocations(unsigned int num_refills,
|
||||
size_t allocated_size,
|
||||
size_t gc_waste,
|
||||
size_t refill_waste);
|
||||
void update_slow_allocations(unsigned int allocations);
|
||||
void update_num_slow_allocations(unsigned int num_slow_allocations);
|
||||
void update(const ThreadLocalAllocStats& other);
|
||||
|
||||
void reset();
|
||||
|
||||
@ -82,7 +82,7 @@ void ThreadLocalAllocBuffer::record_slow_allocation(size_t obj_size) {
|
||||
|
||||
set_refill_waste_limit(refill_waste_limit() + refill_waste_limit_increment());
|
||||
|
||||
_slow_allocations++;
|
||||
_num_slow_allocations++;
|
||||
|
||||
log_develop_trace(gc, tlab)("TLAB: %s thread: " PTR_FORMAT " [id: %2d]"
|
||||
" obj: %zu"
|
||||
|
||||
@ -450,8 +450,8 @@
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _slow_allocations, unsigned) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _num_refills, unsigned) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _num_slow_allocations, unsigned) \
|
||||
\
|
||||
nonstatic_field(SafepointMechanism::ThreadData, _polling_word, volatile uintptr_t) \
|
||||
nonstatic_field(SafepointMechanism::ThreadData, _polling_page, volatile uintptr_t) \
|
||||
|
||||
@ -219,12 +219,12 @@ static BufferBlob* initialize_stubs(BlobId blob_id,
|
||||
if (STUBGEN_BLOB_FIELD_NAME(blob_name) == nullptr) { \
|
||||
BlobId blob_id = BlobId:: JOIN3(stubgen, blob_name, id); \
|
||||
int size = _ ## blob_name ## _code_size; \
|
||||
int max_aligned_size = 10; \
|
||||
int max_aligned_stubs = StubInfo::stub_count(blob_id); \
|
||||
const char* timer_msg = "StubRoutines generation " # blob_name " stubs"; \
|
||||
const char* name = "StubRoutines (" # blob_name " stubs)"; \
|
||||
const char* assert_msg = "_" # blob_name "_code_size"; \
|
||||
STUBGEN_BLOB_FIELD_NAME(blob_name) = \
|
||||
initialize_stubs(blob_id, size, max_aligned_size, timer_msg, \
|
||||
initialize_stubs(blob_id, size, max_aligned_stubs, timer_msg, \
|
||||
name, assert_msg); \
|
||||
} \
|
||||
}
|
||||
|
||||
@ -335,11 +335,11 @@
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \
|
||||
static_field(ThreadLocalAllocBuffer, _target_refills, unsigned) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \
|
||||
static_field(ThreadLocalAllocBuffer, _target_num_refills, unsigned) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _num_refills, unsigned) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _refill_waste, unsigned) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _gc_waste, unsigned) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _slow_allocations, unsigned) \
|
||||
nonstatic_field(ThreadLocalAllocBuffer, _num_slow_allocations, unsigned) \
|
||||
nonstatic_field(VirtualSpace, _low_boundary, char*) \
|
||||
nonstatic_field(VirtualSpace, _high_boundary, char*) \
|
||||
nonstatic_field(VirtualSpace, _low, char*) \
|
||||
@ -2142,4 +2142,3 @@ void vmStructs_init() {
|
||||
VMStructs::init();
|
||||
}
|
||||
#endif // ASSERT
|
||||
|
||||
|
||||
@ -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
|
||||
@ -101,7 +101,18 @@ public class DHasKEM implements KEMSpi {
|
||||
return new KEM.Encapsulated(
|
||||
sub(dh, from, to),
|
||||
pkEm, null);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ECDH validation failure
|
||||
// all-zero shared secret
|
||||
throw e;
|
||||
} catch (InvalidKeyException e) {
|
||||
// Invalid peer public key
|
||||
// Convert InvalidKeyException to an unchecked exception
|
||||
throw new IllegalArgumentException("Invalid peer public key",
|
||||
e);
|
||||
} catch (Exception e) {
|
||||
// Unexpected internal failure
|
||||
throw new ProviderException("internal error", e);
|
||||
}
|
||||
}
|
||||
@ -126,6 +137,11 @@ public class DHasKEM implements KEMSpi {
|
||||
PublicKey pkE = params.DeserializePublicKey(encapsulation);
|
||||
SecretKey dh = params.DH(algorithm, skR, pkE);
|
||||
return sub(dh, from, to);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ECDH validation failure
|
||||
// all-zero shared secret
|
||||
throw e;
|
||||
} catch (IOException | InvalidKeyException e) {
|
||||
throw new DecapsulateException("Cannot decapsulate", e);
|
||||
} catch (Exception e) {
|
||||
@ -248,7 +264,24 @@ public class DHasKEM implements KEMSpi {
|
||||
KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm);
|
||||
ka.init(skE);
|
||||
ka.doPhase(pkR, true);
|
||||
return ka.generateSecret(alg);
|
||||
SecretKey secret = ka.generateSecret(alg);
|
||||
|
||||
// RFC 8446 section 7.4.2: checks for all-zero
|
||||
// X25519/X448 shared secret.
|
||||
if (kaAlgorithm.equals("X25519") ||
|
||||
kaAlgorithm.equals("X448")) {
|
||||
byte[] s = secret.getEncoded();
|
||||
for (byte b : s) {
|
||||
if (b != 0) {
|
||||
return secret;
|
||||
}
|
||||
}
|
||||
// Trigger ILLEGAL_PARAMETER alert
|
||||
throw new IllegalArgumentException(
|
||||
"All-zero shared secret");
|
||||
}
|
||||
|
||||
return secret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -26,6 +26,7 @@ package sun.security.ssl;
|
||||
|
||||
import sun.security.util.RawKeySpec;
|
||||
|
||||
import javax.crypto.DecapsulateException;
|
||||
import javax.crypto.KDF;
|
||||
import javax.crypto.KEM;
|
||||
import javax.crypto.KeyAgreement;
|
||||
@ -35,6 +36,7 @@ import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Provider;
|
||||
@ -173,6 +175,9 @@ public class KAKeyDerivation implements SSLKeyDerivation {
|
||||
"encapsulation");
|
||||
}
|
||||
|
||||
// All exceptions thrown during KEM encapsulation are mapped
|
||||
// to TLS fatal alerts:
|
||||
// illegal_parameter alert or internal_error alert.
|
||||
try {
|
||||
KeyFactory kf = (provider != null) ?
|
||||
KeyFactory.getInstance(algorithmName, provider) :
|
||||
@ -189,8 +194,18 @@ public class KAKeyDerivation implements SSLKeyDerivation {
|
||||
SecretKey derived = deriveHandshakeSecret(algorithm, sharedSecret);
|
||||
|
||||
return new KEM.Encapsulated(derived, enc.encapsulation(), null);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new SSLHandshakeException("Could not generate secret", gse);
|
||||
} catch (IllegalArgumentException | InvalidKeyException e) {
|
||||
// Peer validation failure
|
||||
// ECDH all-zero shared secret (RFC 8446 section 7.4.2),
|
||||
// ML-KEM encapsulation key check failure (FIPS-203 section 7.2)
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
// Cryptographic failure,
|
||||
// deriveHandshakeSecret failure.
|
||||
throw context.conContext.fatal(Alert.INTERNAL_ERROR, e);
|
||||
} catch (RuntimeException e) {
|
||||
// unexpected provider/runtime failure
|
||||
throw context.conContext.fatal(Alert.INTERNAL_ERROR, e);
|
||||
} finally {
|
||||
KeyUtil.destroySecretKeys(sharedSecret);
|
||||
}
|
||||
@ -208,13 +223,30 @@ public class KAKeyDerivation implements SSLKeyDerivation {
|
||||
// Using KEM: called by the client after receiving the KEM
|
||||
// ciphertext (keyshare) from the server in ServerHello.
|
||||
// The client decapsulates it using its private key.
|
||||
KEM kem = (provider != null)
|
||||
? KEM.getInstance(algorithmName, provider)
|
||||
: KEM.getInstance(algorithmName);
|
||||
var decapsulator = kem.newDecapsulator(localPrivateKey);
|
||||
sharedSecret = decapsulator.decapsulate(
|
||||
keyshare, 0, decapsulator.secretSize(),
|
||||
"TlsPremasterSecret");
|
||||
|
||||
// All exceptions thrown during KEM decapsulation are mapped
|
||||
// to TLS fatal alerts:
|
||||
// illegal_parameter alert or internal_error alert.
|
||||
try {
|
||||
KEM kem = (provider != null)
|
||||
? KEM.getInstance(algorithmName, provider)
|
||||
: KEM.getInstance(algorithmName);
|
||||
var decapsulator = kem.newDecapsulator(localPrivateKey);
|
||||
sharedSecret = decapsulator.decapsulate(
|
||||
keyshare, 0, decapsulator.secretSize(),
|
||||
"TlsPremasterSecret");
|
||||
} catch (IllegalArgumentException | InvalidKeyException |
|
||||
DecapsulateException e) {
|
||||
// Peer validation failure
|
||||
// ECDH all-zero shared secret (RFC 8446 section 7.4.2)
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
// cryptographic failure
|
||||
throw context.conContext.fatal(Alert.INTERNAL_ERROR, e);
|
||||
} catch (RuntimeException e) {
|
||||
// unexpected provider/runtime failure
|
||||
throw context.conContext.fatal(Alert.INTERNAL_ERROR, e);
|
||||
}
|
||||
} else {
|
||||
// Using traditional DH-style Key Agreement
|
||||
KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
|
||||
@ -225,6 +257,7 @@ public class KAKeyDerivation implements SSLKeyDerivation {
|
||||
|
||||
return deriveHandshakeSecret(type, sharedSecret);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
// deriveHandshakeSecret() failure
|
||||
throw new SSLHandshakeException("Could not generate secret", gse);
|
||||
} finally {
|
||||
KeyUtil.destroySecretKeys(sharedSecret);
|
||||
|
||||
@ -134,6 +134,16 @@ The following commands are available:
|
||||
|
||||
- `-all`: (Optional) Show help for all commands (BOOLEAN, false) .
|
||||
|
||||
`AOT.end_recording`
|
||||
: Ends an in-progress AOT training and records the results to the file(s) specified by `-XX:AOTConfiguration` and/or `-XX:AOTCacheOutput`.
|
||||
|
||||
Impact: Low
|
||||
|
||||
**Note:**
|
||||
|
||||
The JVM must be started in AOT training mode using command-line arguments such as `-XX:AOTMode=record` or `-XX:AOTCacheOutput=<file>`.
|
||||
The results of the AOT training can be an AOT configuration file, an AOT cache file, or both.
|
||||
|
||||
`Compiler.CodeHeap_Analytics` \[*function*\] \[*granularity*\]
|
||||
: Print CodeHeap analytics
|
||||
|
||||
|
||||
@ -21,7 +21,6 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
@ -30,20 +29,20 @@ import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.ProviderMismatchException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/*
|
||||
* @test
|
||||
@ -55,47 +54,48 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||
*/
|
||||
public class PathOps {
|
||||
|
||||
static FileSystem fs;
|
||||
// create empty JAR file, testing doesn't require any contents
|
||||
static Path emptyJar;
|
||||
|
||||
// This test uses a static file system since some ops tested on
|
||||
// Path depend on the same underlying `fs` instance
|
||||
@BeforeAll
|
||||
static void setup() throws IOException {
|
||||
// create empty JAR file, test doesn't require any contents
|
||||
Path emptyJar = Utils.createJarFile("empty.jar");
|
||||
fs = FileSystems.newFileSystem(emptyJar);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void cleanup() throws IOException {
|
||||
fs.close();
|
||||
emptyJar = Utils.createJarFile("empty.jar");
|
||||
}
|
||||
|
||||
// Ensure NPEs are thrown for null inputs on Path ops
|
||||
@Test
|
||||
void nullPointerTest() {
|
||||
Path path = fs.getPath("foo");
|
||||
assertThrows(NullPointerException.class, () -> path.resolve((String) null));
|
||||
assertThrows(NullPointerException.class, () -> path.relativize(null));
|
||||
assertThrows(NullPointerException.class, () -> path.compareTo(null));
|
||||
assertThrows(NullPointerException.class, () -> path.startsWith((Path) null));
|
||||
assertThrows(NullPointerException.class, () -> path.endsWith((Path) null));
|
||||
void nullPointerTest() throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
Path path = fs.getPath("foo");
|
||||
assertThrows(NullPointerException.class, () -> path.resolve((String) null));
|
||||
assertThrows(NullPointerException.class, () -> path.relativize(null));
|
||||
assertThrows(NullPointerException.class, () -> path.compareTo(null));
|
||||
assertThrows(NullPointerException.class, () -> path.startsWith((Path) null));
|
||||
assertThrows(NullPointerException.class, () -> path.endsWith((Path) null));
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure correct behavior when paths are provided by mismatched providers
|
||||
@Test
|
||||
void mismatchedProvidersTest() {
|
||||
Path path = fs.getPath("foo");
|
||||
void mismatchedProvidersTest() throws IOException {
|
||||
Path other = Paths.get("foo");
|
||||
assertThrows(ProviderMismatchException.class, () -> path.compareTo(other));
|
||||
assertThrows(ProviderMismatchException.class, () -> path.resolve(other));
|
||||
assertThrows(ProviderMismatchException.class, () -> path.relativize(other));
|
||||
assertFalse(path.startsWith(other), "providerMismatched startsWith() returns true ");
|
||||
assertFalse(path.endsWith(other), "providerMismatched endsWith() returns true ");
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
Path path = fs.getPath("foo");
|
||||
assertThrows(ProviderMismatchException.class, () -> path.compareTo(other));
|
||||
assertThrows(ProviderMismatchException.class, () -> path.resolve(other));
|
||||
assertThrows(ProviderMismatchException.class, () -> path.relativize(other));
|
||||
assertFalse(path.startsWith(other), "providerMismatched startsWith() returns true ");
|
||||
assertFalse(path.endsWith(other), "providerMismatched endsWith() returns true ");
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure correct construction of paths when given sequence of strings
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void constructionTest(String first, String[] more, String expected) {
|
||||
string(getPath(first, more), expected);
|
||||
void constructionTest(String first, String[] more, String expected) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
string(fs.getPath(first, more), expected);
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> constructionTest() {
|
||||
@ -112,22 +112,29 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure proper root, parent, and name components
|
||||
@Test
|
||||
void allComponentsTest() {
|
||||
var path = getPath("/a/b/c");
|
||||
root(path, "/");
|
||||
parent(path, "/a/b");
|
||||
name(path, "c");
|
||||
void allComponentsTest() throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath("/a/b/c");
|
||||
root(path, "/");
|
||||
parent(path, "/a/b");
|
||||
name(path, "c");
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure correct name count for root only and empty name
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void nameCountTest(String first, String root, String parent, String name, int nameCount) {
|
||||
var path = getPath(first);
|
||||
root(path, root);
|
||||
parent(path, parent);
|
||||
name(path, name);
|
||||
nameCount(path, nameCount);
|
||||
void nameCountTest(String first, String root, String parent, String name, int nameCount) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
root(path, root);
|
||||
parent(path, parent);
|
||||
name(path, name);
|
||||
assertNotNull(path);
|
||||
assertEquals(nameCount, path.getNameCount());
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> nameCountTest() {
|
||||
@ -139,13 +146,16 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure correct parent and name behavior for no root and name only
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void parentNameTest(String first, String root, String parent, String name) {
|
||||
var path = getPath(first);
|
||||
root(path, root);
|
||||
parent(path, parent);
|
||||
name(path, name);
|
||||
void parentNameTest(String first, String root, String parent, String name) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
root(path, root);
|
||||
parent(path, parent);
|
||||
name(path, name);
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> parentNameTest() {
|
||||
@ -157,10 +167,16 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure correct (positive) `startsWith` behavior
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void startsWithTest(String first, String prefix) {
|
||||
starts(getPath(first), prefix);
|
||||
void startsWithTest(String first, String prefix) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
assertNotNull(path);
|
||||
Path s = fs.getPath(prefix);
|
||||
assertTrue(path.startsWith(s));
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> startsWithTest() {
|
||||
@ -180,10 +196,16 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure correct (negative) `startsWith` behavior
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void notStartsWithTest(String first, String prefix) {
|
||||
notStarts(getPath(first), prefix);
|
||||
void notStartsWithTest(String first, String prefix) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
assertNotNull(path);
|
||||
Path s = fs.getPath(prefix);
|
||||
assertFalse(path.startsWith(s));
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> notStartsWithTest() {
|
||||
@ -203,10 +225,16 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure correct (positive) `endsWith` behavior
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void endsWithTest(String first, String suffix) {
|
||||
ends(getPath(first), suffix);
|
||||
void endsWithTest(String first, String suffix) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
assertNotNull(path);
|
||||
Path s = fs.getPath(suffix);
|
||||
assertTrue(path.endsWith(s));
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> endsWithTest() {
|
||||
@ -231,10 +259,16 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure correct (negative) `endsWith` behavior
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void notEndsWithTest(String first, String suffix) {
|
||||
notEnds(getPath(first), suffix);
|
||||
void notEndsWithTest(String first, String suffix) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
assertNotNull(path);
|
||||
Path s = fs.getPath(suffix);
|
||||
assertFalse(path.endsWith(s));
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> notEndsWithTest() {
|
||||
@ -248,10 +282,15 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure `getName` returns correct String at index
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void elementTest(int index, String expected) {
|
||||
element(getPath("a/b/c"), index, expected);
|
||||
void elementTest(int index, String expected) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath("a/b/c");
|
||||
assertNotNull(path);
|
||||
assertEquals(expected, path.getName(index).toString());
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> elementTest() {
|
||||
@ -262,22 +301,35 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure expected behavior for absolute paths
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"/", "/tmp"} )
|
||||
void isAbsoluteTest(String first) {
|
||||
absolute(getPath(first));
|
||||
void isAbsoluteTest(String first) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
assertNotNull(path);
|
||||
assertTrue(path.isAbsolute());
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure expected behavior for non-absolute paths
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"tmp", ""} )
|
||||
void notAbsoluteTest(String first) {
|
||||
notAbsolute(getPath(first));
|
||||
void notAbsoluteTest(String first) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
assertNotNull(path);
|
||||
assertFalse(path.isAbsolute());
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure correct append and replacement behavior for `resolve(String)`
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void resolveTest(String first, String other, String expected) {
|
||||
resolve(getPath(first), other, expected);
|
||||
void resolveTest(String first, String other, String expected) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
resolve(fs.getPath(first), other, expected);
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> resolveTest() {
|
||||
@ -298,10 +350,15 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure correct append and replacement behavior for `resolve(Path)`
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void resolvePathTest(String first, String other, String expected) {
|
||||
resolvePath(getPath(first), other, expected);
|
||||
void resolvePathTest(String first, String other, String expected) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
assertNotNull(path);
|
||||
assertEquals(expected, path.resolve(fs.getPath(other)).toString());
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> resolvePathTest() {
|
||||
@ -322,10 +379,13 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure correct behavior for `resolveSibling`
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void resolveSiblingTest(String first, String other, String expected) {
|
||||
resolveSibling(getPath(first), other, expected);
|
||||
void resolveSiblingTest(String first, String other, String expected) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
resolveSibling(fs.getPath(first), other, expected);
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> resolveSiblingTest() {
|
||||
@ -345,18 +405,28 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Checking `resolve` and `resolveSibling` behavior for empty path
|
||||
@Test
|
||||
void resolveSiblingAndResolveTest() {
|
||||
var path = getPath("");
|
||||
resolveSibling(path, "foo", "foo");
|
||||
resolveSibling(path, "/foo", "/foo");
|
||||
resolve(path, "", "");
|
||||
void resolveSiblingAndResolveTest() throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath("");
|
||||
resolveSibling(path, "foo", "foo");
|
||||
resolveSibling(path, "/foo", "/foo");
|
||||
resolve(path, "", "");
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure correct behavior of `relativize`. i.e. Relative path should be
|
||||
// produced between two given paths
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void relativizeTest(String first, String other, String expected) {
|
||||
relativize(getPath(first), other, expected);
|
||||
void relativizeTest(String first, String other, String expected) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
assertNotNull(path);
|
||||
Path that = fs.getPath(other);
|
||||
assertEquals(expected, path.relativize(that).toString());
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> relativizeTest() {
|
||||
@ -380,10 +450,15 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure correct behavior of `normalize`. i.e. redundant elements should be removed.
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void normalizeTest(String first, String expected) {
|
||||
normalize(getPath(first), expected);
|
||||
void normalizeTest(String first, String expected) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath(first);
|
||||
assertNotNull(path);
|
||||
assertEquals(expected, path.normalize().toString());
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> normalizeTest() {
|
||||
@ -409,10 +484,13 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Check IPE is thrown for invalid path Strings
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void invalidTest(String first) {
|
||||
assertThrows(InvalidPathException.class, () -> getPath(first));
|
||||
void invalidTest(String first) throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
assertThrows(InvalidPathException.class, () -> fs.getPath(first));
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<String> invalidTest() {
|
||||
@ -426,184 +504,65 @@ public class PathOps {
|
||||
);
|
||||
}
|
||||
|
||||
// Check that repeated forward slash is normalized correctly
|
||||
@Test
|
||||
void normalizationTest() {
|
||||
var path = getPath("//foo//bar");
|
||||
string(path, "/foo/bar");
|
||||
root(path, "/");
|
||||
parent(path, "/foo");
|
||||
name(path, "bar");
|
||||
void normalizationTest() throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath("//foo//bar");
|
||||
string(path, "/foo/bar");
|
||||
root(path, "/");
|
||||
parent(path, "/foo");
|
||||
name(path, "bar");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void isSameFileTest() {
|
||||
isSameFile(getPath("/fileDoesNotExist"), "/fileDoesNotExist");
|
||||
@Test // Check that identical paths refer to the same file
|
||||
void isSameFileTest() throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
var path = fs.getPath("/fileDoesNotExist");
|
||||
assertNotNull(path);
|
||||
assertTrue(Files.isSameFile(path, fs.getPath("/fileDoesNotExist")));
|
||||
}
|
||||
}
|
||||
|
||||
// Regression test for 8139956: Ensure `relativize` of equivalent paths
|
||||
// produces an empty path -> `getNameCount` returns 1
|
||||
@Test
|
||||
void getNameCountTest() {
|
||||
// 8139956
|
||||
System.out.println("check getNameCount");
|
||||
int nc = fs.getPath("/").relativize(fs.getPath("/")).getNameCount();
|
||||
assertEquals(1, nc, "getNameCount of empty path failed");
|
||||
void getNameCountTest() throws IOException {
|
||||
try (var fs = FileSystems.newFileSystem(emptyJar)) {
|
||||
int nc = fs.getPath("/").relativize(fs.getPath("/")).getNameCount();
|
||||
assertEquals(1, nc, "getNameCount of empty path failed");
|
||||
}
|
||||
}
|
||||
|
||||
// Utilities for testing
|
||||
|
||||
static void checkPath(Path path) {
|
||||
assertNotNull(path, "path is null");
|
||||
}
|
||||
|
||||
static void check(Object result, String expected) {
|
||||
if (result == null) {
|
||||
if (expected == null) return;
|
||||
} else {
|
||||
// compare string representations
|
||||
if (expected != null && result.toString().equals(expected))
|
||||
return;
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
static void check(Object result, boolean expected) {
|
||||
check(result, Boolean.toString(expected));
|
||||
}
|
||||
|
||||
static void check(Object result, int expected) {
|
||||
check(result, Integer.toString(expected));
|
||||
}
|
||||
|
||||
static void root(Path path, String expected) {
|
||||
System.out.println("check root");
|
||||
checkPath(path);
|
||||
check(path.getRoot(), expected);
|
||||
assertNotNull(path);
|
||||
assertEquals(expected, Objects.toString(path.getRoot(), null));
|
||||
}
|
||||
|
||||
static void parent(Path path, String expected) {
|
||||
System.out.println("check parent");
|
||||
checkPath(path);
|
||||
check(path.getParent(), expected);
|
||||
assertNotNull(path);
|
||||
assertEquals(expected, Objects.toString(path.getParent(), null));
|
||||
}
|
||||
|
||||
static void name(Path path, String expected) {
|
||||
System.out.println("check name");
|
||||
checkPath(path);
|
||||
check(path.getFileName(), expected);
|
||||
}
|
||||
|
||||
static void nameCount(Path path, int expected) {
|
||||
System.out.println("check nameCount");
|
||||
checkPath(path);
|
||||
check(path.getNameCount(), expected);
|
||||
}
|
||||
|
||||
static void element(Path path, int index, String expected) {
|
||||
System.out.format("check element %d\n", index);
|
||||
checkPath(path);
|
||||
check(path.getName(index), expected);
|
||||
}
|
||||
|
||||
static void subpath(Path path, int startIndex, int endIndex, String expected) {
|
||||
System.out.format("test subpath(%d,%d)\n", startIndex, endIndex);
|
||||
checkPath(path);
|
||||
check(path.subpath(startIndex, endIndex), expected);
|
||||
}
|
||||
|
||||
static void starts(Path path, String prefix) {
|
||||
System.out.format("test startsWith with %s\n", prefix);
|
||||
checkPath(path);
|
||||
Path s = fs.getPath(prefix);
|
||||
check(path.startsWith(s), true);
|
||||
}
|
||||
|
||||
static void notStarts(Path path, String prefix) {
|
||||
System.out.format("test not startsWith with %s\n", prefix);
|
||||
checkPath(path);
|
||||
Path s = fs.getPath(prefix);
|
||||
check(path.startsWith(s), false);
|
||||
}
|
||||
|
||||
static void ends(Path path, String suffix) {
|
||||
System.out.format("test endsWith %s\n", suffix);
|
||||
checkPath(path);
|
||||
Path s = fs.getPath(suffix);
|
||||
check(path.endsWith(s), true);
|
||||
}
|
||||
|
||||
static void notEnds(Path path, String suffix) {
|
||||
System.out.format("test not endsWith %s\n", suffix);
|
||||
checkPath(path);
|
||||
Path s = fs.getPath(suffix);
|
||||
check(path.endsWith(s), false);
|
||||
}
|
||||
|
||||
static void absolute(Path path) {
|
||||
System.out.println("check path is absolute");
|
||||
checkPath(path);
|
||||
check(path.isAbsolute(), true);
|
||||
}
|
||||
|
||||
static void notAbsolute(Path path) {
|
||||
System.out.println("check path is not absolute");
|
||||
checkPath(path);
|
||||
check(path.isAbsolute(), false);
|
||||
assertNotNull(path);
|
||||
assertEquals(expected, Objects.toString(path.getFileName(), null));
|
||||
}
|
||||
|
||||
static void resolve(Path path, String other, String expected) {
|
||||
System.out.format("test resolve %s\n", other);
|
||||
checkPath(path);
|
||||
check(path.resolve(other), expected);
|
||||
}
|
||||
|
||||
static void resolvePath(Path path, String other, String expected) {
|
||||
System.out.format("test resolve %s\n", other);
|
||||
checkPath(path);
|
||||
check(path.resolve(fs.getPath(other)), expected);
|
||||
assertNotNull(path);
|
||||
assertEquals(expected, path.resolve(other).toString());
|
||||
}
|
||||
|
||||
static void resolveSibling(Path path, String other, String expected) {
|
||||
System.out.format("test resolveSibling %s\n", other);
|
||||
checkPath(path);
|
||||
check(path.resolveSibling(other), expected);
|
||||
|
||||
}
|
||||
|
||||
static void relativize(Path path, String other, String expected) {
|
||||
System.out.format("test relativize %s\n", other);
|
||||
checkPath(path);
|
||||
Path that = fs.getPath(other);
|
||||
check(path.relativize(that), expected);
|
||||
|
||||
}
|
||||
|
||||
static void normalize(Path path, String expected) {
|
||||
System.out.println("check normalized path");
|
||||
checkPath(path);
|
||||
check(path.normalize(), expected);
|
||||
|
||||
assertNotNull(path);
|
||||
assertEquals(expected, path.resolveSibling(other).toString());
|
||||
}
|
||||
|
||||
static void string(Path path, String expected) {
|
||||
System.out.println("check string representation");
|
||||
checkPath(path);
|
||||
check(path, expected);
|
||||
}
|
||||
|
||||
static void isSameFile(Path path, String target) {
|
||||
try {
|
||||
System.out.println("check two paths are same");
|
||||
checkPath(path);
|
||||
check(Files.isSameFile(path, fs.getPath(target)), true);
|
||||
} catch (IOException ioe) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
static Path getPath(String s) {
|
||||
return fs.getPath(s);
|
||||
}
|
||||
|
||||
static Path getPath(String first, String... more) {
|
||||
return fs.getPath(first, more);
|
||||
assertNotNull(path);
|
||||
assertEquals(expected, path.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 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,6 +29,7 @@
|
||||
/*
|
||||
* @test
|
||||
* @bug 8273553 8253368
|
||||
* @library /test/lib
|
||||
* @summary sun.security.ssl.SSLEngineImpl.closeInbound also has similar error
|
||||
* of JDK-8253368
|
||||
* @run main/othervm SSLSocketSSLEngineCloseInbound TLSv1.3
|
||||
@ -79,12 +80,30 @@
|
||||
* read() ... ChangeCipherSpec
|
||||
* read() ... Finished
|
||||
*/
|
||||
import javax.net.ssl.*;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.security.*;
|
||||
import java.nio.*;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.KeyStore;
|
||||
import java.security.Security;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class SSLSocketSSLEngineCloseInbound {
|
||||
|
||||
@ -124,6 +143,8 @@ public class SSLSocketSSLEngineCloseInbound {
|
||||
private ByteBuffer cTOs; // "reliable" transport client->server
|
||||
private ByteBuffer sTOc; // "reliable" transport server->client
|
||||
|
||||
private final CountDownLatch serverReadyLatch = new CountDownLatch(1);
|
||||
|
||||
/*
|
||||
* The following is to set up the keystores/trust material.
|
||||
*/
|
||||
@ -224,7 +245,7 @@ public class SSLSocketSSLEngineCloseInbound {
|
||||
|
||||
// server-side socket that will read
|
||||
try (Socket socket = serverSocket.accept()) {
|
||||
socket.setSoTimeout(500);
|
||||
socket.setSoTimeout((int)Utils.adjustTimeout(500));
|
||||
|
||||
InputStream is = socket.getInputStream();
|
||||
OutputStream os = socket.getOutputStream();
|
||||
@ -289,6 +310,8 @@ public class SSLSocketSSLEngineCloseInbound {
|
||||
throw new Exception("Server session is not valid");
|
||||
}
|
||||
|
||||
// Server signals client it has finished writing
|
||||
serverReadyLatch.countDown();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -315,6 +338,8 @@ public class SSLSocketSSLEngineCloseInbound {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Release the latch if an exception is thrown.
|
||||
serverReadyLatch.countDown();
|
||||
if (serverException != null) {
|
||||
if (clientException != null) {
|
||||
serverException.initCause(clientException);
|
||||
@ -342,7 +367,8 @@ public class SSLSocketSSLEngineCloseInbound {
|
||||
public void run() {
|
||||
// client-side socket
|
||||
try (SSLSocket sslSocket = (SSLSocket)sslc.getSocketFactory().
|
||||
createSocket("localhost", port)) {
|
||||
createSocket(InetAddress.getLoopbackAddress(),
|
||||
port)) {
|
||||
clientSocket = sslSocket;
|
||||
|
||||
OutputStream os = sslSocket.getOutputStream();
|
||||
@ -365,9 +391,9 @@ public class SSLSocketSSLEngineCloseInbound {
|
||||
throw new Exception("Client's session is not valid");
|
||||
}
|
||||
|
||||
// Give server a chance to read before we shutdown via
|
||||
// the try-with-resources block.
|
||||
Thread.sleep(2000);
|
||||
// Client waits for server to finish sending data
|
||||
// before shutdown.
|
||||
serverReadyLatch.await();
|
||||
} catch (Exception e) {
|
||||
clientException = e;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user