提交 c993be01 编写于 作者: A apetushkov

8220293: Deadlock in JFR string pool

Reviewed-by: rehn, egahlin
上级 f1429fec
/* /*
* 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. * 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
...@@ -135,6 +135,8 @@ void JfrCheckpointManager::register_service_thread(const Thread* thread) { ...@@ -135,6 +135,8 @@ void JfrCheckpointManager::register_service_thread(const Thread* thread) {
void JfrCheckpointManager::register_full(BufferPtr t, Thread* thread) { void JfrCheckpointManager::register_full(BufferPtr t, Thread* thread) {
// nothing here at the moment // nothing here at the moment
assert(t != NULL, "invariant");
assert(t->acquired_by(thread), "invariant");
assert(t->retired(), "invariant"); assert(t->retired(), "invariant");
} }
......
/* /*
* 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. * 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
...@@ -137,6 +137,14 @@ void JfrBuffer::clear_identity() { ...@@ -137,6 +137,14 @@ void JfrBuffer::clear_identity() {
_identity = NULL; _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 #ifdef ASSERT
static bool validate_to(const JfrBuffer* const to, size_t size) { static bool validate_to(const JfrBuffer* const to, size_t size) {
assert(to != NULL, "invariant"); assert(to != NULL, "invariant");
...@@ -154,10 +162,6 @@ static bool validate_this(const JfrBuffer* const t, size_t size) { ...@@ -154,10 +162,6 @@ static bool validate_this(const JfrBuffer* const t, size_t size) {
assert(t->top() + size <= t->pos(), "invariant"); assert(t->top() + size <= t->pos(), "invariant");
return true; return true;
} }
bool JfrBuffer::acquired_by_self() const {
return identity() == Thread::current();
}
#endif // ASSERT #endif // ASSERT
void JfrBuffer::move(JfrBuffer* const to, size_t size) { void JfrBuffer::move(JfrBuffer* const to, size_t size) {
...@@ -184,7 +188,6 @@ void JfrBuffer::concurrent_move_and_reinitialize(JfrBuffer* const to, size_t siz ...@@ -184,7 +188,6 @@ void JfrBuffer::concurrent_move_and_reinitialize(JfrBuffer* const to, size_t siz
set_concurrent_top(start()); set_concurrent_top(start());
} }
// flags
enum FLAG { enum FLAG {
RETIRED = 1, RETIRED = 1,
TRANSIENT = 2, TRANSIENT = 2,
......
...@@ -57,7 +57,6 @@ class JfrBuffer { ...@@ -57,7 +57,6 @@ class JfrBuffer {
u4 _size; u4 _size;
const u1* stable_top() const; const u1* stable_top() const;
void clear_flags();
public: public:
JfrBuffer(); JfrBuffer();
...@@ -150,6 +149,8 @@ class JfrBuffer { ...@@ -150,6 +149,8 @@ class JfrBuffer {
void acquire(const void* id); void acquire(const void* id);
bool try_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 release();
void move(JfrBuffer* const to, size_t size); void move(JfrBuffer* const to, size_t size);
...@@ -166,8 +167,6 @@ class JfrBuffer { ...@@ -166,8 +167,6 @@ class JfrBuffer {
bool retired() const; bool retired() const;
void set_retired(); void set_retired();
void clear_retired(); void clear_retired();
debug_only(bool acquired_by_self() const;)
}; };
class JfrAgeNode : public JfrBuffer { class JfrAgeNode : public JfrBuffer {
......
...@@ -346,19 +346,19 @@ inline void process_free_list(Processor& processor, Mspace* mspace, jfr_iter_dir ...@@ -346,19 +346,19 @@ inline void process_free_list(Processor& processor, Mspace* mspace, jfr_iter_dir
template <typename Mspace> template <typename Mspace>
inline bool ReleaseOp<Mspace>::process(typename Mspace::Type* t) { inline bool ReleaseOp<Mspace>::process(typename Mspace::Type* t) {
assert(t != NULL, "invariant"); assert(t != NULL, "invariant");
if (t->retired() || t->try_acquire(_thread)) { // assumes some means of exclusive access to t
if (t->transient()) { if (t->transient()) {
if (_release_full) { if (_release_full) {
mspace_release_full_critical(t, _mspace); mspace_release_full_critical(t, _mspace);
} else { } else {
mspace_release_free_critical(t, _mspace); mspace_release_free_critical(t, _mspace);
}
return true;
} }
t->reinitialize(); return true;
assert(t->empty(), "invariant");
t->release(); // publish
} }
t->reinitialize();
assert(t->empty(), "invariant");
assert(!t->retired(), "invariant");
t->release(); // publish
return true; return true;
} }
......
...@@ -340,9 +340,9 @@ static bool full_buffer_registration(BufferPtr buffer, JfrStorageAgeMspace* age_ ...@@ -340,9 +340,9 @@ static bool full_buffer_registration(BufferPtr buffer, JfrStorageAgeMspace* age_
void JfrStorage::register_full(BufferPtr buffer, Thread* thread) { void JfrStorage::register_full(BufferPtr buffer, Thread* thread) {
assert(buffer != NULL, "invariant"); assert(buffer != NULL, "invariant");
assert(buffer->retired(), "invariant"); assert(buffer->retired(), "invariant");
assert(buffer->acquired_by(thread), "invariant");
if (!full_buffer_registration(buffer, _age_mspace, control(), thread)) { if (!full_buffer_registration(buffer, _age_mspace, control(), thread)) {
handle_registration_failure(buffer); handle_registration_failure(buffer);
buffer->release();
} }
if (control().should_post_buffer_full_message()) { if (control().should_post_buffer_full_message()) {
_post_box.post(MSG_FULLBUFFER); _post_box.post(MSG_FULLBUFFER);
...@@ -377,8 +377,8 @@ void JfrStorage::release(BufferPtr buffer, Thread* thread) { ...@@ -377,8 +377,8 @@ void JfrStorage::release(BufferPtr buffer, Thread* thread) {
} }
} }
assert(buffer->empty(), "invariant"); assert(buffer->empty(), "invariant");
assert(buffer->identity() != NULL, "invariant");
control().increment_dead(); control().increment_dead();
buffer->release();
buffer->set_retired(); buffer->set_retired();
} }
...@@ -733,13 +733,14 @@ public: ...@@ -733,13 +733,14 @@ public:
Scavenger(JfrStorageControl& control, Mspace* mspace) : _control(control), _mspace(mspace), _count(0), _amount(0) {} Scavenger(JfrStorageControl& control, Mspace* mspace) : _control(control), _mspace(mspace), _count(0), _amount(0) {}
bool process(Type* t) { bool process(Type* t) {
if (t->retired()) { if (t->retired()) {
assert(t->identity() != NULL, "invariant");
assert(t->empty(), "invariant");
assert(!t->transient(), "invariant"); assert(!t->transient(), "invariant");
assert(!t->lease(), "invariant"); assert(!t->lease(), "invariant");
assert(t->empty(), "invariant");
assert(t->identity() == NULL, "invariant");
++_count; ++_count;
_amount += t->total_size(); _amount += t->total_size();
t->clear_retired(); t->clear_retired();
t->release();
_control.decrement_dead(); _control.decrement_dead();
mspace_release_full_critical(t, _mspace); mspace_release_full_critical(t, _mspace);
} }
......
...@@ -92,7 +92,6 @@ class ConcurrentWriteOpExcludeRetired : private ConcurrentWriteOp<Operation> { ...@@ -92,7 +92,6 @@ class ConcurrentWriteOpExcludeRetired : private ConcurrentWriteOp<Operation> {
size_t processed() const { return ConcurrentWriteOp<Operation>::processed(); } size_t processed() const { return ConcurrentWriteOp<Operation>::processed(); }
}; };
template <typename Operation> template <typename Operation>
class MutexedWriteOp { class MutexedWriteOp {
private: private:
...@@ -104,6 +103,15 @@ class MutexedWriteOp { ...@@ -104,6 +103,15 @@ class MutexedWriteOp {
size_t processed() const { return _operation.processed(); } 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 { enum jfr_operation_mode {
mutexed = 1, mutexed = 1,
concurrent concurrent
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#define SHARE_VM_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_INLINE_HPP #define SHARE_VM_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_INLINE_HPP
#include "jfr/recorder/storage/jfrStorageUtils.hpp" #include "jfr/recorder/storage/jfrStorageUtils.hpp"
#include "runtime/thread.inline.hpp"
template <typename T> template <typename T>
inline bool UnBufferedWriteToChunk<T>::write(T* t, const u1* data, size_t size) { 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) { ...@@ -75,6 +76,28 @@ inline bool MutexedWriteOp<Operation>::process(typename Operation::Type* t) {
return result; 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> template <typename Operation>
inline bool DiscardOp<Operation>::process(typename Operation::Type* t) { inline bool DiscardOp<Operation>::process(typename Operation::Type* t) {
assert(t != NULL, "invariant"); assert(t != NULL, "invariant");
......
/* /*
* 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. * 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
...@@ -139,93 +139,76 @@ bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) { ...@@ -139,93 +139,76 @@ bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) {
return current_epoch; return current_epoch;
} }
class StringPoolWriteOp { template <template <typename> class Operation>
class StringPoolOp {
public: public:
typedef JfrStringPoolBuffer Type; typedef JfrStringPoolBuffer Type;
private: private:
UnBufferedWriteToChunk<Type> _writer; Operation<Type> _op;
Thread* _thread; Thread* _thread;
size_t _strings_processed; size_t _strings_processed;
public: 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) { 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(); const uint64_t nof_strings_used = buffer->string_count();
assert(nof_strings_used > 0, "invariant"); assert(nof_strings_used > 0, "invariant");
buffer->set_string_top(buffer->string_top() + nof_strings_used); buffer->set_string_top(buffer->string_top() + nof_strings_used);
// "size processed" for string pool buffers is the number of processed string elements // "size processed" for string pool buffers is the number of processed string elements
_strings_processed += nof_strings_used; _strings_processed += nof_strings_used;
const bool ret = _writer.write(buffer, data, size); return _op.write(buffer, data, size);
buffer->release();
return ret;
} }
size_t processed() { return _strings_processed; } size_t processed() { return _strings_processed; }
}; };
typedef StringPoolWriteOp WriteOperation; template <typename Type>
typedef ConcurrentWriteOp<WriteOperation> ConcurrentWriteOperation; class StringPoolDiscarderStub {
public:
size_t JfrStringPool::write() { bool write(Type* buffer, const u1* data, size_t size) {
Thread* const thread = Thread::current(); // stub only, discard happens at higher level
WriteOperation wo(_chunkwriter, thread); return true;
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 StringPoolOp<UnBufferedWriteToChunk> WriteOperation;
typedef StringPoolOp<StringPoolDiscarderStub> DiscardOperation;
typedef ExclusiveOp<WriteOperation> ExclusiveWriteOperation;
typedef ExclusiveOp<DiscardOperation> ExclusiveDiscardOperation;
typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation; typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation;
typedef CompositeOperation<MutexedWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation; typedef CompositeOperation<ExclusiveWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation;
typedef CompositeOperation<ExclusiveDiscardOperation, StringPoolReleaseOperation> StringPoolDiscardOperation;
size_t JfrStringPool::write_at_safepoint() { size_t JfrStringPool::write() {
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
Thread* const thread = Thread::current(); Thread* const thread = Thread::current();
WriteOperation wo(_chunkwriter, thread); WriteOperation wo(_chunkwriter, thread);
MutexedWriteOperation mwo(wo); ExclusiveWriteOperation ewo(wo);
StringPoolReleaseOperation spro(_free_list_mspace, thread, false); StringPoolReleaseOperation spro(_free_list_mspace, thread, false);
StringPoolWriteOperation spwo(&mwo, &spro); StringPoolWriteOperation spwo(&ewo, &spro);
assert(_free_list_mspace->is_full_empty(), "invariant"); assert(_free_list_mspace->is_full_empty(), "invariant");
process_free_list(spwo, _free_list_mspace); process_free_list(spwo, _free_list_mspace);
return wo.processed(); return wo.processed();
} }
class StringPoolBufferDiscarder { size_t JfrStringPool::write_at_safepoint() {
private: assert(SafepointSynchronize::is_at_safepoint(), "invariant");
Thread* _thread; return write();
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::clear() { 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"); 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(); return discard_operation.processed();
} }
void JfrStringPool::register_full(BufferPtr t, Thread* thread) { void JfrStringPool::register_full(BufferPtr t, Thread* thread) {
// nothing here at the moment // nothing here at the moment
assert(t != NULL, "invariant");
assert(t->acquired_by(thread), "invariant");
assert(t->retired(), "invariant"); assert(t->retired(), "invariant");
} }
......
/* /*
* 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. * 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
...@@ -29,11 +29,9 @@ JfrStringPoolBuffer::JfrStringPoolBuffer() : JfrBuffer(), _string_count_pos(0), ...@@ -29,11 +29,9 @@ JfrStringPoolBuffer::JfrStringPoolBuffer() : JfrBuffer(), _string_count_pos(0),
void JfrStringPoolBuffer::reinitialize() { void JfrStringPoolBuffer::reinitialize() {
assert(acquired_by_self() || retired(), "invariant"); assert(acquired_by_self() || retired(), "invariant");
concurrent_top();
set_pos((start()));
set_string_pos(0); set_string_pos(0);
set_string_top(0); set_string_top(0);
set_concurrent_top(start()); JfrBuffer::reinitialize();
} }
uint64_t JfrStringPoolBuffer::string_pos() const { uint64_t JfrStringPoolBuffer::string_pos() const {
...@@ -57,7 +55,7 @@ void JfrStringPoolBuffer::set_string_pos(uint64_t value) { ...@@ -57,7 +55,7 @@ void JfrStringPoolBuffer::set_string_pos(uint64_t value) {
} }
void JfrStringPoolBuffer::increment(uint64_t value) { void JfrStringPoolBuffer::increment(uint64_t value) {
assert(acquired_by_self() || retired(), "invariant"); assert(acquired_by_self(), "invariant");
++_string_count_pos; ++_string_count_pos;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册