mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-08 04:29:28 +00:00
8220293: Deadlock in JFR string pool
Reviewed-by: rehn, egahlin
This commit is contained in:
parent
55ba58b1c8
commit
0b9afd7a5a
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2019, 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
|
||||
@ -136,6 +136,8 @@ void JfrCheckpointManager::register_service_thread(const Thread* thread) {
|
||||
|
||||
void JfrCheckpointManager::register_full(BufferPtr t, Thread* thread) {
|
||||
// nothing here at the moment
|
||||
assert(t != NULL, "invariant");
|
||||
assert(t->acquired_by(thread), "invariant");
|
||||
assert(t->retired(), "invariant");
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2019, 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
|
||||
@ -136,6 +136,14 @@ void JfrBuffer::clear_identity() {
|
||||
_identity = NULL;
|
||||
}
|
||||
|
||||
bool JfrBuffer::acquired_by(const void* id) const {
|
||||
return identity() == id;
|
||||
}
|
||||
|
||||
bool JfrBuffer::acquired_by_self() const {
|
||||
return acquired_by(Thread::current());
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
static bool validate_to(const JfrBuffer* const to, size_t size) {
|
||||
assert(to != NULL, "invariant");
|
||||
@ -153,10 +161,6 @@ static bool validate_this(const JfrBuffer* const t, size_t size) {
|
||||
assert(t->top() + size <= t->pos(), "invariant");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JfrBuffer::acquired_by_self() const {
|
||||
return identity() == Thread::current();
|
||||
}
|
||||
#endif // ASSERT
|
||||
|
||||
void JfrBuffer::move(JfrBuffer* const to, size_t size) {
|
||||
@ -183,7 +187,6 @@ void JfrBuffer::concurrent_move_and_reinitialize(JfrBuffer* const to, size_t siz
|
||||
set_concurrent_top(start());
|
||||
}
|
||||
|
||||
// flags
|
||||
enum FLAG {
|
||||
RETIRED = 1,
|
||||
TRANSIENT = 2,
|
||||
|
||||
@ -57,7 +57,6 @@ class JfrBuffer {
|
||||
u4 _size;
|
||||
|
||||
const u1* stable_top() const;
|
||||
void clear_flags();
|
||||
|
||||
public:
|
||||
JfrBuffer();
|
||||
@ -150,6 +149,8 @@ class JfrBuffer {
|
||||
|
||||
void acquire(const void* id);
|
||||
bool try_acquire(const void* id);
|
||||
bool acquired_by(const void* id) const;
|
||||
bool acquired_by_self() const;
|
||||
void release();
|
||||
|
||||
void move(JfrBuffer* const to, size_t size);
|
||||
@ -166,8 +167,6 @@ class JfrBuffer {
|
||||
bool retired() const;
|
||||
void set_retired();
|
||||
void clear_retired();
|
||||
|
||||
debug_only(bool acquired_by_self() const;)
|
||||
};
|
||||
|
||||
class JfrAgeNode : public JfrBuffer {
|
||||
|
||||
@ -346,19 +346,19 @@ inline void process_free_list(Processor& processor, Mspace* mspace, jfr_iter_dir
|
||||
template <typename Mspace>
|
||||
inline bool ReleaseOp<Mspace>::process(typename Mspace::Type* t) {
|
||||
assert(t != NULL, "invariant");
|
||||
if (t->retired() || t->try_acquire(_thread)) {
|
||||
if (t->transient()) {
|
||||
if (_release_full) {
|
||||
mspace_release_full_critical(t, _mspace);
|
||||
} else {
|
||||
mspace_release_free_critical(t, _mspace);
|
||||
}
|
||||
return true;
|
||||
// assumes some means of exclusive access to t
|
||||
if (t->transient()) {
|
||||
if (_release_full) {
|
||||
mspace_release_full_critical(t, _mspace);
|
||||
} else {
|
||||
mspace_release_free_critical(t, _mspace);
|
||||
}
|
||||
t->reinitialize();
|
||||
assert(t->empty(), "invariant");
|
||||
t->release(); // publish
|
||||
return true;
|
||||
}
|
||||
t->reinitialize();
|
||||
assert(t->empty(), "invariant");
|
||||
assert(!t->retired(), "invariant");
|
||||
t->release(); // publish
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -339,9 +339,9 @@ static bool full_buffer_registration(BufferPtr buffer, JfrStorageAgeMspace* age_
|
||||
void JfrStorage::register_full(BufferPtr buffer, Thread* thread) {
|
||||
assert(buffer != NULL, "invariant");
|
||||
assert(buffer->retired(), "invariant");
|
||||
assert(buffer->acquired_by(thread), "invariant");
|
||||
if (!full_buffer_registration(buffer, _age_mspace, control(), thread)) {
|
||||
handle_registration_failure(buffer);
|
||||
buffer->release();
|
||||
}
|
||||
if (control().should_post_buffer_full_message()) {
|
||||
_post_box.post(MSG_FULLBUFFER);
|
||||
@ -376,8 +376,8 @@ void JfrStorage::release(BufferPtr buffer, Thread* thread) {
|
||||
}
|
||||
}
|
||||
assert(buffer->empty(), "invariant");
|
||||
assert(buffer->identity() != NULL, "invariant");
|
||||
control().increment_dead();
|
||||
buffer->release();
|
||||
buffer->set_retired();
|
||||
}
|
||||
|
||||
@ -738,13 +738,14 @@ public:
|
||||
Scavenger(JfrStorageControl& control, Mspace* mspace) : _control(control), _mspace(mspace), _count(0), _amount(0) {}
|
||||
bool process(Type* t) {
|
||||
if (t->retired()) {
|
||||
assert(t->identity() != NULL, "invariant");
|
||||
assert(t->empty(), "invariant");
|
||||
assert(!t->transient(), "invariant");
|
||||
assert(!t->lease(), "invariant");
|
||||
assert(t->empty(), "invariant");
|
||||
assert(t->identity() == NULL, "invariant");
|
||||
++_count;
|
||||
_amount += t->total_size();
|
||||
t->clear_retired();
|
||||
t->release();
|
||||
_control.decrement_dead();
|
||||
mspace_release_full_critical(t, _mspace);
|
||||
}
|
||||
|
||||
@ -92,7 +92,6 @@ class ConcurrentWriteOpExcludeRetired : private ConcurrentWriteOp<Operation> {
|
||||
size_t processed() const { return ConcurrentWriteOp<Operation>::processed(); }
|
||||
};
|
||||
|
||||
|
||||
template <typename Operation>
|
||||
class MutexedWriteOp {
|
||||
private:
|
||||
@ -104,6 +103,15 @@ class MutexedWriteOp {
|
||||
size_t processed() const { return _operation.processed(); }
|
||||
};
|
||||
|
||||
template <typename Operation>
|
||||
class ExclusiveOp : private MutexedWriteOp<Operation> {
|
||||
public:
|
||||
typedef typename Operation::Type Type;
|
||||
ExclusiveOp(Operation& operation) : MutexedWriteOp<Operation>(operation) {}
|
||||
bool process(Type* t);
|
||||
size_t processed() const { return MutexedWriteOp<Operation>::processed(); }
|
||||
};
|
||||
|
||||
enum jfr_operation_mode {
|
||||
mutexed = 1,
|
||||
concurrent
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#define SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_INLINE_HPP
|
||||
|
||||
#include "jfr/recorder/storage/jfrStorageUtils.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
|
||||
template <typename T>
|
||||
inline bool UnBufferedWriteToChunk<T>::write(T* t, const u1* data, size_t size) {
|
||||
@ -75,6 +76,28 @@ inline bool MutexedWriteOp<Operation>::process(typename Operation::Type* t) {
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
static void retired_sensitive_acquire(Type* t) {
|
||||
assert(t != NULL, "invariant");
|
||||
if (t->retired()) {
|
||||
return;
|
||||
}
|
||||
Thread* const thread = Thread::current();
|
||||
while (!t->try_acquire(thread)) {
|
||||
if (t->retired()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Operation>
|
||||
inline bool ExclusiveOp<Operation>::process(typename Operation::Type* t) {
|
||||
retired_sensitive_acquire(t);
|
||||
assert(t->acquired_by_self() || t->retired(), "invariant");
|
||||
// User is required to ensure proper release of the acquisition
|
||||
return MutexedWriteOp<Operation>::process(t);
|
||||
}
|
||||
|
||||
template <typename Operation>
|
||||
inline bool DiscardOp<Operation>::process(typename Operation::Type* t) {
|
||||
assert(t != NULL, "invariant");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2019, 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
|
||||
@ -140,93 +140,76 @@ bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) {
|
||||
return current_epoch;
|
||||
}
|
||||
|
||||
class StringPoolWriteOp {
|
||||
template <template <typename> class Operation>
|
||||
class StringPoolOp {
|
||||
public:
|
||||
typedef JfrStringPoolBuffer Type;
|
||||
private:
|
||||
UnBufferedWriteToChunk<Type> _writer;
|
||||
Operation<Type> _op;
|
||||
Thread* _thread;
|
||||
size_t _strings_processed;
|
||||
public:
|
||||
StringPoolWriteOp(JfrChunkWriter& writer, Thread* thread) : _writer(writer), _thread(thread), _strings_processed(0) {}
|
||||
StringPoolOp() : _op(), _thread(Thread::current()), _strings_processed(0) {}
|
||||
StringPoolOp(JfrChunkWriter& writer, Thread* thread) : _op(writer), _thread(thread), _strings_processed(0) {}
|
||||
bool write(Type* buffer, const u1* data, size_t size) {
|
||||
buffer->acquire(_thread); // blocking
|
||||
assert(buffer->acquired_by(_thread) || buffer->retired(), "invariant");
|
||||
const uint64_t nof_strings_used = buffer->string_count();
|
||||
assert(nof_strings_used > 0, "invariant");
|
||||
buffer->set_string_top(buffer->string_top() + nof_strings_used);
|
||||
// "size processed" for string pool buffers is the number of processed string elements
|
||||
_strings_processed += nof_strings_used;
|
||||
const bool ret = _writer.write(buffer, data, size);
|
||||
buffer->release();
|
||||
return ret;
|
||||
return _op.write(buffer, data, size);
|
||||
}
|
||||
size_t processed() { return _strings_processed; }
|
||||
};
|
||||
|
||||
typedef StringPoolWriteOp WriteOperation;
|
||||
typedef ConcurrentWriteOp<WriteOperation> ConcurrentWriteOperation;
|
||||
template <typename Type>
|
||||
class StringPoolDiscarderStub {
|
||||
public:
|
||||
bool write(Type* buffer, const u1* data, size_t size) {
|
||||
// stub only, discard happens at higher level
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
typedef StringPoolOp<UnBufferedWriteToChunk> WriteOperation;
|
||||
typedef StringPoolOp<StringPoolDiscarderStub> DiscardOperation;
|
||||
typedef ExclusiveOp<WriteOperation> ExclusiveWriteOperation;
|
||||
typedef ExclusiveOp<DiscardOperation> ExclusiveDiscardOperation;
|
||||
typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation;
|
||||
typedef CompositeOperation<ExclusiveWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation;
|
||||
typedef CompositeOperation<ExclusiveDiscardOperation, StringPoolReleaseOperation> StringPoolDiscardOperation;
|
||||
|
||||
size_t JfrStringPool::write() {
|
||||
Thread* const thread = Thread::current();
|
||||
WriteOperation wo(_chunkwriter, thread);
|
||||
ConcurrentWriteOperation cwo(wo);
|
||||
assert(_free_list_mspace->is_full_empty(), "invariant");
|
||||
process_free_list(cwo, _free_list_mspace);
|
||||
return wo.processed();
|
||||
}
|
||||
|
||||
typedef MutexedWriteOp<WriteOperation> MutexedWriteOperation;
|
||||
typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation;
|
||||
typedef CompositeOperation<MutexedWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation;
|
||||
|
||||
size_t JfrStringPool::write_at_safepoint() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||
Thread* const thread = Thread::current();
|
||||
WriteOperation wo(_chunkwriter, thread);
|
||||
MutexedWriteOperation mwo(wo);
|
||||
ExclusiveWriteOperation ewo(wo);
|
||||
StringPoolReleaseOperation spro(_free_list_mspace, thread, false);
|
||||
StringPoolWriteOperation spwo(&mwo, &spro);
|
||||
StringPoolWriteOperation spwo(&ewo, &spro);
|
||||
assert(_free_list_mspace->is_full_empty(), "invariant");
|
||||
process_free_list(spwo, _free_list_mspace);
|
||||
return wo.processed();
|
||||
}
|
||||
|
||||
class StringPoolBufferDiscarder {
|
||||
private:
|
||||
Thread* _thread;
|
||||
size_t _processed;
|
||||
public:
|
||||
typedef JfrStringPoolBuffer Type;
|
||||
StringPoolBufferDiscarder() : _thread(Thread::current()), _processed(0) {}
|
||||
bool process(Type* buffer) {
|
||||
buffer->acquire(_thread); // serialized access
|
||||
const u1* const current_top = buffer->top();
|
||||
const size_t unflushed_size = buffer->pos() - current_top;
|
||||
if (unflushed_size == 0) {
|
||||
assert(buffer->string_count() == 0, "invariant");
|
||||
buffer->release();
|
||||
return true;
|
||||
}
|
||||
buffer->set_top(current_top + unflushed_size);
|
||||
const uint64_t nof_strings_used = buffer->string_count();
|
||||
buffer->set_string_top(buffer->string_top() + nof_strings_used);
|
||||
// "size processed" for string pool buffers is the number of string elements
|
||||
_processed += (size_t)nof_strings_used;
|
||||
buffer->release();
|
||||
return true;
|
||||
}
|
||||
size_t processed() const { return _processed; }
|
||||
};
|
||||
size_t JfrStringPool::write_at_safepoint() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||
return write();
|
||||
}
|
||||
|
||||
size_t JfrStringPool::clear() {
|
||||
StringPoolBufferDiscarder discard_operation;
|
||||
DiscardOperation discard_operation;
|
||||
ExclusiveDiscardOperation edo(discard_operation);
|
||||
StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false);
|
||||
StringPoolDiscardOperation spdo(&edo, &spro);
|
||||
assert(_free_list_mspace->is_full_empty(), "invariant");
|
||||
process_free_list(discard_operation, _free_list_mspace);
|
||||
process_free_list(spdo, _free_list_mspace);
|
||||
return discard_operation.processed();
|
||||
}
|
||||
|
||||
void JfrStringPool::register_full(BufferPtr t, Thread* thread) {
|
||||
// nothing here at the moment
|
||||
assert(t != NULL, "invariant");
|
||||
assert(t->acquired_by(thread), "invariant");
|
||||
assert(t->retired(), "invariant");
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2019, 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,11 +29,9 @@ JfrStringPoolBuffer::JfrStringPoolBuffer() : JfrBuffer(), _string_count_pos(0),
|
||||
|
||||
void JfrStringPoolBuffer::reinitialize() {
|
||||
assert(acquired_by_self() || retired(), "invariant");
|
||||
concurrent_top();
|
||||
set_pos((start()));
|
||||
set_string_pos(0);
|
||||
set_string_top(0);
|
||||
set_concurrent_top(start());
|
||||
JfrBuffer::reinitialize();
|
||||
}
|
||||
|
||||
uint64_t JfrStringPoolBuffer::string_pos() const {
|
||||
@ -57,7 +55,7 @@ void JfrStringPoolBuffer::set_string_pos(uint64_t value) {
|
||||
}
|
||||
|
||||
void JfrStringPoolBuffer::increment(uint64_t value) {
|
||||
assert(acquired_by_self() || retired(), "invariant");
|
||||
assert(acquired_by_self(), "invariant");
|
||||
++_string_count_pos;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user