8309862: Unsafe list operations in JfrStringPool

Reviewed-by: egahlin
This commit is contained in:
Markus Grönlund 2023-06-13 11:47:47 +00:00
parent 4f23fc1f27
commit 05f896a153
5 changed files with 61 additions and 30 deletions

View File

@ -338,18 +338,20 @@ static size_t write_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) {
return invoke(fs); return invoke(fs);
} }
typedef Content<JfrStringPool, &JfrStringPool::write> StringPool; typedef Content<JfrStringPool, &JfrStringPool::flush> FlushStringPoolFunctor;
typedef WriteCheckpointEvent<StringPool> WriteStringPool; typedef Content<JfrStringPool, &JfrStringPool::write> WriteStringPoolFunctor;
typedef WriteCheckpointEvent<FlushStringPoolFunctor> FlushStringPool;
typedef WriteCheckpointEvent<WriteStringPoolFunctor> WriteStringPool;
static u4 flush_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { static u4 flush_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
StringPool sp(string_pool); FlushStringPoolFunctor fspf(string_pool);
WriteStringPool wsp(chunkwriter, sp, TYPE_STRING); FlushStringPool fsp(chunkwriter, fspf, TYPE_STRING);
return invoke(wsp); return invoke(fsp);
} }
static u4 write_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { static u4 write_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
StringPool sp(string_pool); WriteStringPoolFunctor wspf(string_pool);
WriteStringPool wsp(chunkwriter, sp, TYPE_STRING); WriteStringPool wsp(chunkwriter, wspf, TYPE_STRING);
return invoke(wsp); return invoke(wsp);
} }
@ -461,7 +463,6 @@ void JfrRecorderService::clear() {
void JfrRecorderService::pre_safepoint_clear() { void JfrRecorderService::pre_safepoint_clear() {
clear_object_allocation_sampling(); clear_object_allocation_sampling();
_string_pool.clear();
_storage.clear(); _storage.clear();
JfrStackTraceRepository::clear(); JfrStackTraceRepository::clear();
} }
@ -475,7 +476,6 @@ void JfrRecorderService::invoke_safepoint_clear() {
void JfrRecorderService::safepoint_clear() { void JfrRecorderService::safepoint_clear() {
assert(SafepointSynchronize::is_at_safepoint(), "invariant"); assert(SafepointSynchronize::is_at_safepoint(), "invariant");
_checkpoint_manager.begin_epoch_shift(); _checkpoint_manager.begin_epoch_shift();
_string_pool.clear();
_storage.clear(); _storage.clear();
_chunkwriter.set_time_stamp(); _chunkwriter.set_time_stamp();
JfrStackTraceRepository::clear(); JfrStackTraceRepository::clear();
@ -483,6 +483,7 @@ void JfrRecorderService::safepoint_clear() {
} }
void JfrRecorderService::post_safepoint_clear() { void JfrRecorderService::post_safepoint_clear() {
_string_pool.clear();
_checkpoint_manager.clear(); _checkpoint_manager.clear();
} }
@ -567,9 +568,6 @@ void JfrRecorderService::pre_safepoint_write() {
// The sampler is released (unlocked) later in post_safepoint_write. // The sampler is released (unlocked) later in post_safepoint_write.
ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire()); ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire());
} }
if (_string_pool.is_modified()) {
write_stringpool(_string_pool, _chunkwriter);
}
write_storage(_storage, _chunkwriter); write_storage(_storage, _chunkwriter);
if (_stack_trace_repository.is_modified()) { if (_stack_trace_repository.is_modified()) {
write_stacktrace(_stack_trace_repository, _chunkwriter, false); write_stacktrace(_stack_trace_repository, _chunkwriter, false);
@ -587,9 +585,6 @@ void JfrRecorderService::safepoint_write() {
assert(SafepointSynchronize::is_at_safepoint(), "invariant"); assert(SafepointSynchronize::is_at_safepoint(), "invariant");
_checkpoint_manager.begin_epoch_shift(); _checkpoint_manager.begin_epoch_shift();
JfrStackTraceRepository::clear_leak_profiler(); JfrStackTraceRepository::clear_leak_profiler();
if (_string_pool.is_modified()) {
write_stringpool(_string_pool, _chunkwriter);
}
_checkpoint_manager.on_rotation(); _checkpoint_manager.on_rotation();
_storage.write_at_safepoint(); _storage.write_at_safepoint();
_chunkwriter.set_time_stamp(); _chunkwriter.set_time_stamp();
@ -603,6 +598,7 @@ void JfrRecorderService::post_safepoint_write() {
// Type tagging is epoch relative which entails we are able to write out the // Type tagging is epoch relative which entails we are able to write out the
// already tagged artifacts for the previous epoch. We can accomplish this concurrently // already tagged artifacts for the previous epoch. We can accomplish this concurrently
// with threads now tagging artifacts in relation to the new, now updated, epoch and remain outside of a safepoint. // with threads now tagging artifacts in relation to the new, now updated, epoch and remain outside of a safepoint.
write_stringpool(_string_pool, _chunkwriter);
_checkpoint_manager.write_type_set(); _checkpoint_manager.write_type_set();
if (LeakProfiler::is_running()) { if (LeakProfiler::is_running()) {
// The object sampler instance was exclusively acquired and locked in pre_safepoint_write. // The object sampler instance was exclusively acquired and locked in pre_safepoint_write.

View File

@ -208,4 +208,11 @@ class EpochDispatchOp {
size_t elements() const { return _elements; } size_t elements() const { return _elements; }
}; };
template <typename T>
class ReinitializationOp {
public:
typedef T Type;
bool process(Type* t);
};
#endif // SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_HPP #endif // SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_HPP

View File

@ -175,4 +175,13 @@ size_t EpochDispatchOp<Operation>::dispatch(bool previous_epoch, const u1* eleme
return elements; return elements;
} }
template <typename T>
bool ReinitializationOp<T>::process(T* t) {
assert(t != nullptr, "invariant");
assert(t->identity() != nullptr, "invariant");
t->reinitialize();
t->release();
return true;
}
#endif // SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_INLINE_HPP #endif // SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_INLINE_HPP

View File

@ -77,10 +77,17 @@ static const size_t string_pool_buffer_size = 512 * K;
bool JfrStringPool::initialize() { bool JfrStringPool::initialize() {
assert(_mspace == nullptr, "invariant"); assert(_mspace == nullptr, "invariant");
_mspace = create_mspace<JfrStringPoolMspace>(string_pool_buffer_size, _mspace = create_mspace<JfrStringPoolMspace>(string_pool_buffer_size,
string_pool_cache_count, // cache limit 0,
string_pool_cache_count, // cache preallocate count 0, // cache preallocate count
false, // preallocate_to_free_list (== preallocate directly to live list) false,
this); this);
// preallocate buffer count to each of the epoch live lists
for (size_t i = 0; i < string_pool_cache_count * 2; ++i) {
Buffer* const buffer = mspace_allocate(string_pool_buffer_size, _mspace);
_mspace->add_to_live_list(buffer, i % 2 == 0);
}
assert(_mspace->free_list_is_empty(), "invariant");
return _mspace != nullptr; return _mspace != nullptr;
} }
@ -95,12 +102,8 @@ static void release(BufferPtr buffer, Thread* thread) {
assert(buffer->lease(), "invariant"); assert(buffer->lease(), "invariant");
assert(buffer->acquired_by_self(), "invariant"); assert(buffer->acquired_by_self(), "invariant");
buffer->clear_lease(); buffer->clear_lease();
if (buffer->transient()) {
buffer->set_retired();
} else {
buffer->release(); buffer->release();
} }
}
BufferPtr JfrStringPool::flush(BufferPtr old, size_t used, size_t requested, Thread* thread) { BufferPtr JfrStringPool::flush(BufferPtr old, size_t used, size_t requested, Thread* thread) {
assert(old != nullptr, "invariant"); assert(old != nullptr, "invariant");
@ -180,8 +183,10 @@ typedef StringPoolOp<UnBufferedWriteToChunk> WriteOperation;
typedef StringPoolOp<StringPoolDiscarderStub> DiscardOperation; typedef StringPoolOp<StringPoolDiscarderStub> DiscardOperation;
typedef ExclusiveOp<WriteOperation> ExclusiveWriteOperation; typedef ExclusiveOp<WriteOperation> ExclusiveWriteOperation;
typedef ExclusiveOp<DiscardOperation> ExclusiveDiscardOperation; typedef ExclusiveOp<DiscardOperation> ExclusiveDiscardOperation;
typedef ReinitializationOp<JfrStringPoolBuffer> ReinitializationOperation;
typedef ReleaseWithExcisionOp<JfrStringPoolMspace, JfrStringPoolMspace::LiveList> ReleaseOperation; typedef ReleaseWithExcisionOp<JfrStringPoolMspace, JfrStringPoolMspace::LiveList> ReleaseOperation;
typedef CompositeOperation<ExclusiveWriteOperation, ReleaseOperation> WriteReleaseOperation; typedef CompositeOperation<ExclusiveWriteOperation, ReleaseOperation> WriteReleaseOperation;
typedef CompositeOperation<ExclusiveWriteOperation, ReinitializationOperation> WriteReinitializeOperation;
typedef CompositeOperation<ExclusiveDiscardOperation, ReleaseOperation> DiscardReleaseOperation; typedef CompositeOperation<ExclusiveDiscardOperation, ReleaseOperation> DiscardReleaseOperation;
size_t JfrStringPool::write() { size_t JfrStringPool::write() {
@ -189,10 +194,22 @@ size_t JfrStringPool::write() {
WriteOperation wo(_chunkwriter, thread); WriteOperation wo(_chunkwriter, thread);
ExclusiveWriteOperation ewo(wo); ExclusiveWriteOperation ewo(wo);
assert(_mspace->free_list_is_empty(), "invariant"); assert(_mspace->free_list_is_empty(), "invariant");
ReleaseOperation ro(_mspace, _mspace->live_list()); ReleaseOperation ro(_mspace, _mspace->live_list(true)); // previous epoch list
WriteReleaseOperation wro(&ewo, &ro); WriteReleaseOperation wro(&ewo, &ro);
assert(_mspace->live_list_is_nonempty(), "invariant"); assert(_mspace->live_list_is_nonempty(), "invariant");
process_live_list(wro, _mspace); process_live_list(wro, _mspace, true); // previous epoch list
return wo.processed();
}
size_t JfrStringPool::flush() {
Thread* const thread = Thread::current();
WriteOperation wo(_chunkwriter, thread);
ExclusiveWriteOperation ewo(wo);
ReinitializationOperation rio;
WriteReinitializeOperation wro(&ewo, &rio);
assert(_mspace->free_list_is_empty(), "invariant");
assert(_mspace->live_list_is_nonempty(), "invariant");
process_live_list(wro, _mspace); // current epoch list
return wo.processed(); return wo.processed();
} }
@ -200,10 +217,10 @@ size_t JfrStringPool::clear() {
DiscardOperation discard_operation; DiscardOperation discard_operation;
ExclusiveDiscardOperation edo(discard_operation); ExclusiveDiscardOperation edo(discard_operation);
assert(_mspace->free_list_is_empty(), "invariant"); assert(_mspace->free_list_is_empty(), "invariant");
ReleaseOperation ro(_mspace, _mspace->live_list()); ReleaseOperation ro(_mspace, _mspace->live_list(true)); // previous epoch list
DiscardReleaseOperation discard_op(&edo, &ro); DiscardReleaseOperation discard_op(&edo, &ro);
assert(_mspace->live_list_is_nonempty(), "invariant"); assert(_mspace->live_list_is_nonempty(), "invariant");
process_live_list(discard_op, _mspace); process_live_list(discard_op, _mspace, true); // previous epoch list
return discard_operation.processed(); return discard_operation.processed();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -35,7 +35,7 @@ class JavaThread;
class JfrChunkWriter; class JfrChunkWriter;
class JfrStringPool; class JfrStringPool;
typedef JfrMemorySpace<JfrStringPool, JfrMspaceRetrieval, JfrLinkedList<JfrStringPoolBuffer> > JfrStringPoolMspace; typedef JfrMemorySpace<JfrStringPool, JfrMspaceRetrieval, JfrLinkedList<JfrStringPoolBuffer>, JfrLinkedList<JfrStringPoolBuffer>, true > JfrStringPoolMspace;
// //
// Although called JfrStringPool, a more succinct description would be // Although called JfrStringPool, a more succinct description would be
@ -45,8 +45,10 @@ typedef JfrMemorySpace<JfrStringPool, JfrMspaceRetrieval, JfrLinkedList<JfrStrin
// //
class JfrStringPool : public JfrCHeapObj { class JfrStringPool : public JfrCHeapObj {
public: public:
size_t write();
size_t clear(); size_t clear();
size_t flush();
size_t write();
static jboolean add(jlong id, jstring string, JavaThread* jt); static jboolean add(jlong id, jstring string, JavaThread* jt);
typedef JfrStringPoolMspace::Node Buffer; typedef JfrStringPoolMspace::Node Buffer;