Merge branch 'openjdk:master' into JDK-8373118

This commit is contained in:
Doug Lea 2026-03-25 17:29:22 -04:00 committed by GitHub
commit f2842dcd29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 436 additions and 378 deletions

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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"

View File

@ -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) \

View File

@ -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); \
} \
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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

View File

@ -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());
}
}

View File

@ -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;
}