8220293: Deadlock in JFR string pool jdk8u262-b08

Wed, 17 Jun 2020 11:43:05 +0300

author
apetushkov
date
Wed, 17 Jun 2020 11:43:05 +0300
changeset 9928
d2c2cd90513e
parent 9927
b273df69fbfe
child 9929
cf6e3496e19a

8220293: Deadlock in JFR string pool
Reviewed-by: rehn, egahlin

src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp file | annotate | diff | comparison | revisions
src/share/vm/jfr/recorder/storage/jfrBuffer.cpp file | annotate | diff | comparison | revisions
src/share/vm/jfr/recorder/storage/jfrBuffer.hpp file | annotate | diff | comparison | revisions
src/share/vm/jfr/recorder/storage/jfrMemorySpace.inline.hpp file | annotate | diff | comparison | revisions
src/share/vm/jfr/recorder/storage/jfrStorage.cpp file | annotate | diff | comparison | revisions
src/share/vm/jfr/recorder/storage/jfrStorageUtils.hpp file | annotate | diff | comparison | revisions
src/share/vm/jfr/recorder/storage/jfrStorageUtils.inline.hpp file | annotate | diff | comparison | revisions
src/share/vm/jfr/recorder/stringpool/jfrStringPool.cpp file | annotate | diff | comparison | revisions
src/share/vm/jfr/recorder/stringpool/jfrStringPoolBuffer.cpp file | annotate | diff | comparison | revisions
     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  

mercurial