Wed, 17 Jun 2020 11:43:05 +0300
8220293: Deadlock in JFR string pool
Reviewed-by: rehn, egahlin
1.1 --- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp Mon Jun 15 20:21:56 2020 +0100 1.2 +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp Wed Jun 17 11:43:05 2020 +0300 1.3 @@ -1,5 +1,5 @@ 1.4 /* 1.5 - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. 1.6 + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. 1.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.8 * 1.9 * This code is free software; you can redistribute it and/or modify it 1.10 @@ -135,6 +135,8 @@ 1.11 1.12 void JfrCheckpointManager::register_full(BufferPtr t, Thread* thread) { 1.13 // nothing here at the moment 1.14 + assert(t != NULL, "invariant"); 1.15 + assert(t->acquired_by(thread), "invariant"); 1.16 assert(t->retired(), "invariant"); 1.17 } 1.18
2.1 --- a/src/share/vm/jfr/recorder/storage/jfrBuffer.cpp Mon Jun 15 20:21:56 2020 +0100 2.2 +++ b/src/share/vm/jfr/recorder/storage/jfrBuffer.cpp Wed Jun 17 11:43:05 2020 +0300 2.3 @@ -1,5 +1,5 @@ 2.4 /* 2.5 - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 2.6 + * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. 2.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.8 * 2.9 * This code is free software; you can redistribute it and/or modify it 2.10 @@ -137,6 +137,14 @@ 2.11 _identity = NULL; 2.12 } 2.13 2.14 +bool JfrBuffer::acquired_by(const void* id) const { 2.15 + return identity() == id; 2.16 +} 2.17 + 2.18 +bool JfrBuffer::acquired_by_self() const { 2.19 + return acquired_by(Thread::current()); 2.20 +} 2.21 + 2.22 #ifdef ASSERT 2.23 static bool validate_to(const JfrBuffer* const to, size_t size) { 2.24 assert(to != NULL, "invariant"); 2.25 @@ -154,10 +162,6 @@ 2.26 assert(t->top() + size <= t->pos(), "invariant"); 2.27 return true; 2.28 } 2.29 - 2.30 -bool JfrBuffer::acquired_by_self() const { 2.31 - return identity() == Thread::current(); 2.32 -} 2.33 #endif // ASSERT 2.34 2.35 void JfrBuffer::move(JfrBuffer* const to, size_t size) { 2.36 @@ -184,7 +188,6 @@ 2.37 set_concurrent_top(start()); 2.38 } 2.39 2.40 -// flags 2.41 enum FLAG { 2.42 RETIRED = 1, 2.43 TRANSIENT = 2,
3.1 --- a/src/share/vm/jfr/recorder/storage/jfrBuffer.hpp Mon Jun 15 20:21:56 2020 +0100 3.2 +++ b/src/share/vm/jfr/recorder/storage/jfrBuffer.hpp Wed Jun 17 11:43:05 2020 +0300 3.3 @@ -57,7 +57,6 @@ 3.4 u4 _size; 3.5 3.6 const u1* stable_top() const; 3.7 - void clear_flags(); 3.8 3.9 public: 3.10 JfrBuffer(); 3.11 @@ -150,6 +149,8 @@ 3.12 3.13 void acquire(const void* id); 3.14 bool try_acquire(const void* id); 3.15 + bool acquired_by(const void* id) const; 3.16 + bool acquired_by_self() const; 3.17 void release(); 3.18 3.19 void move(JfrBuffer* const to, size_t size); 3.20 @@ -166,8 +167,6 @@ 3.21 bool retired() const; 3.22 void set_retired(); 3.23 void clear_retired(); 3.24 - 3.25 - debug_only(bool acquired_by_self() const;) 3.26 }; 3.27 3.28 class JfrAgeNode : public JfrBuffer {
4.1 --- a/src/share/vm/jfr/recorder/storage/jfrMemorySpace.inline.hpp Mon Jun 15 20:21:56 2020 +0100 4.2 +++ b/src/share/vm/jfr/recorder/storage/jfrMemorySpace.inline.hpp Wed Jun 17 11:43:05 2020 +0300 4.3 @@ -346,19 +346,19 @@ 4.4 template <typename Mspace> 4.5 inline bool ReleaseOp<Mspace>::process(typename Mspace::Type* t) { 4.6 assert(t != NULL, "invariant"); 4.7 - if (t->retired() || t->try_acquire(_thread)) { 4.8 - if (t->transient()) { 4.9 - if (_release_full) { 4.10 - mspace_release_full_critical(t, _mspace); 4.11 - } else { 4.12 - mspace_release_free_critical(t, _mspace); 4.13 - } 4.14 - return true; 4.15 + // assumes some means of exclusive access to t 4.16 + if (t->transient()) { 4.17 + if (_release_full) { 4.18 + mspace_release_full_critical(t, _mspace); 4.19 + } else { 4.20 + mspace_release_free_critical(t, _mspace); 4.21 } 4.22 - t->reinitialize(); 4.23 - assert(t->empty(), "invariant"); 4.24 - t->release(); // publish 4.25 + return true; 4.26 } 4.27 + t->reinitialize(); 4.28 + assert(t->empty(), "invariant"); 4.29 + assert(!t->retired(), "invariant"); 4.30 + t->release(); // publish 4.31 return true; 4.32 } 4.33
5.1 --- a/src/share/vm/jfr/recorder/storage/jfrStorage.cpp Mon Jun 15 20:21:56 2020 +0100 5.2 +++ b/src/share/vm/jfr/recorder/storage/jfrStorage.cpp Wed Jun 17 11:43:05 2020 +0300 5.3 @@ -340,9 +340,9 @@ 5.4 void JfrStorage::register_full(BufferPtr buffer, Thread* thread) { 5.5 assert(buffer != NULL, "invariant"); 5.6 assert(buffer->retired(), "invariant"); 5.7 + assert(buffer->acquired_by(thread), "invariant"); 5.8 if (!full_buffer_registration(buffer, _age_mspace, control(), thread)) { 5.9 handle_registration_failure(buffer); 5.10 - buffer->release(); 5.11 } 5.12 if (control().should_post_buffer_full_message()) { 5.13 _post_box.post(MSG_FULLBUFFER); 5.14 @@ -377,8 +377,8 @@ 5.15 } 5.16 } 5.17 assert(buffer->empty(), "invariant"); 5.18 + assert(buffer->identity() != NULL, "invariant"); 5.19 control().increment_dead(); 5.20 - buffer->release(); 5.21 buffer->set_retired(); 5.22 } 5.23 5.24 @@ -733,13 +733,14 @@ 5.25 Scavenger(JfrStorageControl& control, Mspace* mspace) : _control(control), _mspace(mspace), _count(0), _amount(0) {} 5.26 bool process(Type* t) { 5.27 if (t->retired()) { 5.28 + assert(t->identity() != NULL, "invariant"); 5.29 + assert(t->empty(), "invariant"); 5.30 assert(!t->transient(), "invariant"); 5.31 assert(!t->lease(), "invariant"); 5.32 - assert(t->empty(), "invariant"); 5.33 - assert(t->identity() == NULL, "invariant"); 5.34 ++_count; 5.35 _amount += t->total_size(); 5.36 t->clear_retired(); 5.37 + t->release(); 5.38 _control.decrement_dead(); 5.39 mspace_release_full_critical(t, _mspace); 5.40 }
6.1 --- a/src/share/vm/jfr/recorder/storage/jfrStorageUtils.hpp Mon Jun 15 20:21:56 2020 +0100 6.2 +++ b/src/share/vm/jfr/recorder/storage/jfrStorageUtils.hpp Wed Jun 17 11:43:05 2020 +0300 6.3 @@ -92,7 +92,6 @@ 6.4 size_t processed() const { return ConcurrentWriteOp<Operation>::processed(); } 6.5 }; 6.6 6.7 - 6.8 template <typename Operation> 6.9 class MutexedWriteOp { 6.10 private: 6.11 @@ -104,6 +103,15 @@ 6.12 size_t processed() const { return _operation.processed(); } 6.13 }; 6.14 6.15 +template <typename Operation> 6.16 +class ExclusiveOp : private MutexedWriteOp<Operation> { 6.17 + public: 6.18 + typedef typename Operation::Type Type; 6.19 + ExclusiveOp(Operation& operation) : MutexedWriteOp<Operation>(operation) {} 6.20 + bool process(Type* t); 6.21 + size_t processed() const { return MutexedWriteOp<Operation>::processed(); } 6.22 +}; 6.23 + 6.24 enum jfr_operation_mode { 6.25 mutexed = 1, 6.26 concurrent
7.1 --- a/src/share/vm/jfr/recorder/storage/jfrStorageUtils.inline.hpp Mon Jun 15 20:21:56 2020 +0100 7.2 +++ b/src/share/vm/jfr/recorder/storage/jfrStorageUtils.inline.hpp Wed Jun 17 11:43:05 2020 +0300 7.3 @@ -26,6 +26,7 @@ 7.4 #define SHARE_VM_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_INLINE_HPP 7.5 7.6 #include "jfr/recorder/storage/jfrStorageUtils.hpp" 7.7 +#include "runtime/thread.inline.hpp" 7.8 7.9 template <typename T> 7.10 inline bool UnBufferedWriteToChunk<T>::write(T* t, const u1* data, size_t size) { 7.11 @@ -75,6 +76,28 @@ 7.12 return result; 7.13 } 7.14 7.15 +template <typename Type> 7.16 +static void retired_sensitive_acquire(Type* t) { 7.17 + assert(t != NULL, "invariant"); 7.18 + if (t->retired()) { 7.19 + return; 7.20 + } 7.21 + Thread* const thread = Thread::current(); 7.22 + while (!t->try_acquire(thread)) { 7.23 + if (t->retired()) { 7.24 + return; 7.25 + } 7.26 + } 7.27 +} 7.28 + 7.29 +template <typename Operation> 7.30 +inline bool ExclusiveOp<Operation>::process(typename Operation::Type* t) { 7.31 + retired_sensitive_acquire(t); 7.32 + assert(t->acquired_by_self() || t->retired(), "invariant"); 7.33 + // User is required to ensure proper release of the acquisition 7.34 + return MutexedWriteOp<Operation>::process(t); 7.35 +} 7.36 + 7.37 template <typename Operation> 7.38 inline bool DiscardOp<Operation>::process(typename Operation::Type* t) { 7.39 assert(t != NULL, "invariant");
8.1 --- a/src/share/vm/jfr/recorder/stringpool/jfrStringPool.cpp Mon Jun 15 20:21:56 2020 +0100 8.2 +++ b/src/share/vm/jfr/recorder/stringpool/jfrStringPool.cpp Wed Jun 17 11:43:05 2020 +0300 8.3 @@ -1,5 +1,5 @@ 8.4 /* 8.5 - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. 8.6 + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. 8.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 8.8 * 8.9 * This code is free software; you can redistribute it and/or modify it 8.10 @@ -139,93 +139,76 @@ 8.11 return current_epoch; 8.12 } 8.13 8.14 -class StringPoolWriteOp { 8.15 +template <template <typename> class Operation> 8.16 +class StringPoolOp { 8.17 public: 8.18 typedef JfrStringPoolBuffer Type; 8.19 private: 8.20 - UnBufferedWriteToChunk<Type> _writer; 8.21 + Operation<Type> _op; 8.22 Thread* _thread; 8.23 size_t _strings_processed; 8.24 public: 8.25 - StringPoolWriteOp(JfrChunkWriter& writer, Thread* thread) : _writer(writer), _thread(thread), _strings_processed(0) {} 8.26 + StringPoolOp() : _op(), _thread(Thread::current()), _strings_processed(0) {} 8.27 + StringPoolOp(JfrChunkWriter& writer, Thread* thread) : _op(writer), _thread(thread), _strings_processed(0) {} 8.28 bool write(Type* buffer, const u1* data, size_t size) { 8.29 - buffer->acquire(_thread); // blocking 8.30 + assert(buffer->acquired_by(_thread) || buffer->retired(), "invariant"); 8.31 const uint64_t nof_strings_used = buffer->string_count(); 8.32 assert(nof_strings_used > 0, "invariant"); 8.33 buffer->set_string_top(buffer->string_top() + nof_strings_used); 8.34 // "size processed" for string pool buffers is the number of processed string elements 8.35 _strings_processed += nof_strings_used; 8.36 - const bool ret = _writer.write(buffer, data, size); 8.37 - buffer->release(); 8.38 - return ret; 8.39 + return _op.write(buffer, data, size); 8.40 } 8.41 size_t processed() { return _strings_processed; } 8.42 }; 8.43 8.44 -typedef StringPoolWriteOp WriteOperation; 8.45 -typedef ConcurrentWriteOp<WriteOperation> ConcurrentWriteOperation; 8.46 +template <typename Type> 8.47 +class StringPoolDiscarderStub { 8.48 + public: 8.49 + bool write(Type* buffer, const u1* data, size_t size) { 8.50 + // stub only, discard happens at higher level 8.51 + return true; 8.52 + } 8.53 +}; 8.54 + 8.55 +typedef StringPoolOp<UnBufferedWriteToChunk> WriteOperation; 8.56 +typedef StringPoolOp<StringPoolDiscarderStub> DiscardOperation; 8.57 +typedef ExclusiveOp<WriteOperation> ExclusiveWriteOperation; 8.58 +typedef ExclusiveOp<DiscardOperation> ExclusiveDiscardOperation; 8.59 +typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation; 8.60 +typedef CompositeOperation<ExclusiveWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation; 8.61 +typedef CompositeOperation<ExclusiveDiscardOperation, StringPoolReleaseOperation> StringPoolDiscardOperation; 8.62 8.63 size_t JfrStringPool::write() { 8.64 Thread* const thread = Thread::current(); 8.65 WriteOperation wo(_chunkwriter, thread); 8.66 - ConcurrentWriteOperation cwo(wo); 8.67 - assert(_free_list_mspace->is_full_empty(), "invariant"); 8.68 - process_free_list(cwo, _free_list_mspace); 8.69 - return wo.processed(); 8.70 -} 8.71 - 8.72 -typedef MutexedWriteOp<WriteOperation> MutexedWriteOperation; 8.73 -typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation; 8.74 -typedef CompositeOperation<MutexedWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation; 8.75 - 8.76 -size_t JfrStringPool::write_at_safepoint() { 8.77 - assert(SafepointSynchronize::is_at_safepoint(), "invariant"); 8.78 - Thread* const thread = Thread::current(); 8.79 - WriteOperation wo(_chunkwriter, thread); 8.80 - MutexedWriteOperation mwo(wo); 8.81 + ExclusiveWriteOperation ewo(wo); 8.82 StringPoolReleaseOperation spro(_free_list_mspace, thread, false); 8.83 - StringPoolWriteOperation spwo(&mwo, &spro); 8.84 + StringPoolWriteOperation spwo(&ewo, &spro); 8.85 assert(_free_list_mspace->is_full_empty(), "invariant"); 8.86 process_free_list(spwo, _free_list_mspace); 8.87 return wo.processed(); 8.88 } 8.89 8.90 -class StringPoolBufferDiscarder { 8.91 - private: 8.92 - Thread* _thread; 8.93 - size_t _processed; 8.94 - public: 8.95 - typedef JfrStringPoolBuffer Type; 8.96 - StringPoolBufferDiscarder() : _thread(Thread::current()), _processed(0) {} 8.97 - bool process(Type* buffer) { 8.98 - buffer->acquire(_thread); // serialized access 8.99 - const u1* const current_top = buffer->top(); 8.100 - const size_t unflushed_size = buffer->pos() - current_top; 8.101 - if (unflushed_size == 0) { 8.102 - assert(buffer->string_count() == 0, "invariant"); 8.103 - buffer->release(); 8.104 - return true; 8.105 - } 8.106 - buffer->set_top(current_top + unflushed_size); 8.107 - const uint64_t nof_strings_used = buffer->string_count(); 8.108 - buffer->set_string_top(buffer->string_top() + nof_strings_used); 8.109 - // "size processed" for string pool buffers is the number of string elements 8.110 - _processed += (size_t)nof_strings_used; 8.111 - buffer->release(); 8.112 - return true; 8.113 - } 8.114 - size_t processed() const { return _processed; } 8.115 -}; 8.116 +size_t JfrStringPool::write_at_safepoint() { 8.117 + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); 8.118 + return write(); 8.119 +} 8.120 8.121 size_t JfrStringPool::clear() { 8.122 - StringPoolBufferDiscarder discard_operation; 8.123 + DiscardOperation discard_operation; 8.124 + ExclusiveDiscardOperation edo(discard_operation); 8.125 + StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false); 8.126 + StringPoolDiscardOperation spdo(&edo, &spro); 8.127 assert(_free_list_mspace->is_full_empty(), "invariant"); 8.128 - process_free_list(discard_operation, _free_list_mspace); 8.129 + process_free_list(spdo, _free_list_mspace); 8.130 return discard_operation.processed(); 8.131 } 8.132 8.133 void JfrStringPool::register_full(BufferPtr t, Thread* thread) { 8.134 // nothing here at the moment 8.135 + assert(t != NULL, "invariant"); 8.136 + assert(t->acquired_by(thread), "invariant"); 8.137 assert(t->retired(), "invariant"); 8.138 } 8.139
9.1 --- a/src/share/vm/jfr/recorder/stringpool/jfrStringPoolBuffer.cpp Mon Jun 15 20:21:56 2020 +0100 9.2 +++ b/src/share/vm/jfr/recorder/stringpool/jfrStringPoolBuffer.cpp Wed Jun 17 11:43:05 2020 +0300 9.3 @@ -1,5 +1,5 @@ 9.4 /* 9.5 - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. 9.6 + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. 9.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 9.8 * 9.9 * This code is free software; you can redistribute it and/or modify it 9.10 @@ -29,11 +29,9 @@ 9.11 9.12 void JfrStringPoolBuffer::reinitialize() { 9.13 assert(acquired_by_self() || retired(), "invariant"); 9.14 - concurrent_top(); 9.15 - set_pos((start())); 9.16 set_string_pos(0); 9.17 set_string_top(0); 9.18 - set_concurrent_top(start()); 9.19 + JfrBuffer::reinitialize(); 9.20 } 9.21 9.22 uint64_t JfrStringPoolBuffer::string_pos() const { 9.23 @@ -57,7 +55,7 @@ 9.24 } 9.25 9.26 void JfrStringPoolBuffer::increment(uint64_t value) { 9.27 - assert(acquired_by_self() || retired(), "invariant"); 9.28 + assert(acquired_by_self(), "invariant"); 9.29 ++_string_count_pos; 9.30 } 9.31