6915005: G1: Hang in PtrQueueSet::completed_buffers_list_length with gcl001

Wed, 13 Jan 2010 15:45:47 -0800

author
johnc
date
Wed, 13 Jan 2010 15:45:47 -0800
changeset 1604
09646c4656ca
parent 1603
22e27cceb7d8
child 1605
c4d722788ed6

6915005: G1: Hang in PtrQueueSet::completed_buffers_list_length with gcl001
Summary: When enqueuing a completed PtrQueue buffer, cache a local pointer to the buffer and clear the field in the PtrQueue prior to unlocking the mutex referenced by the _lock field and pass the cached local value to the enqueuing routine. This will prevent the same completed buffer being enqueued multiple times, which causes the hang.
Reviewed-by: ysr

src/share/vm/gc_implementation/g1/ptrQueue.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/ptrQueue.inline.hpp file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/vm/gc_implementation/g1/ptrQueue.cpp	Thu Jan 14 09:20:13 2010 -0800
     1.2 +++ b/src/share/vm/gc_implementation/g1/ptrQueue.cpp	Wed Jan 13 15:45:47 2010 -0800
     1.3 @@ -73,7 +73,12 @@
     1.4  
     1.5  void PtrQueue::locking_enqueue_completed_buffer(void** buf) {
     1.6    assert(_lock->owned_by_self(), "Required.");
     1.7 +
     1.8 +  // We have to unlock _lock (which may be Shared_DirtyCardQ_lock) before
     1.9 +  // we acquire DirtyCardQ_CBL_mon inside enqeue_complete_buffer as they
    1.10 +  // have the same rank and we may get the "possible deadlock" message
    1.11    _lock->unlock();
    1.12 +
    1.13    qset()->enqueue_complete_buffer(buf);
    1.14    // We must relock only because the caller will unlock, for the normal
    1.15    // case.
    1.16 @@ -140,7 +145,36 @@
    1.17    // holding the lock if there is one).
    1.18    if (_buf != NULL) {
    1.19      if (_lock) {
    1.20 -      locking_enqueue_completed_buffer(_buf);
    1.21 +      assert(_lock->owned_by_self(), "Required.");
    1.22 +
    1.23 +      // The current PtrQ may be the shared dirty card queue and
    1.24 +      // may be being manipulated by more than one worker thread
    1.25 +      // during a pause. Since the enqueuing of the completed
    1.26 +      // buffer unlocks the Shared_DirtyCardQ_lock more than one
    1.27 +      // worker thread can 'race' on reading the shared queue attributes
    1.28 +      // (_buf and _index) and multiple threads can call into this
    1.29 +      // routine for the same buffer. This will cause the completed
    1.30 +      // buffer to be added to the CBL multiple times.
    1.31 +
    1.32 +      // We "claim" the current buffer by caching value of _buf in
    1.33 +      // a local and clearing the field while holding _lock. When
    1.34 +      // _lock is released (while enqueueing the completed buffer)
    1.35 +      // the thread that acquires _lock will skip this code,
    1.36 +      // preventing the subsequent the multiple enqueue, and
    1.37 +      // install a newly allocated buffer below.
    1.38 +
    1.39 +      void** buf = _buf;   // local pointer to completed buffer
    1.40 +      _buf = NULL;         // clear shared _buf field
    1.41 +
    1.42 +      locking_enqueue_completed_buffer(buf);  // enqueue completed buffer
    1.43 +
    1.44 +      // While the current thread was enqueuing the buffer another thread
    1.45 +      // may have a allocated a new buffer and inserted it into this pointer
    1.46 +      // queue. If that happens then we just return so that the current
    1.47 +      // thread doesn't overwrite the buffer allocated by the other thread
    1.48 +      // and potentially losing some dirtied cards.
    1.49 +
    1.50 +      if (_buf != NULL) return;
    1.51      } else {
    1.52        if (qset()->process_or_enqueue_complete_buffer(_buf)) {
    1.53          // Recycle the buffer. No allocation.
     2.1 --- a/src/share/vm/gc_implementation/g1/ptrQueue.inline.hpp	Thu Jan 14 09:20:13 2010 -0800
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,41 +0,0 @@
     2.4 -/*
     2.5 - * Copyright 2001-2007 Sun Microsystems, Inc.  All Rights Reserved.
     2.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     2.7 - *
     2.8 - * This code is free software; you can redistribute it and/or modify it
     2.9 - * under the terms of the GNU General Public License version 2 only, as
    2.10 - * published by the Free Software Foundation.
    2.11 - *
    2.12 - * This code is distributed in the hope that it will be useful, but WITHOUT
    2.13 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    2.14 - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    2.15 - * version 2 for more details (a copy is included in the LICENSE file that
    2.16 - * accompanied this code).
    2.17 - *
    2.18 - * You should have received a copy of the GNU General Public License version
    2.19 - * 2 along with this work; if not, write to the Free Software Foundation,
    2.20 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    2.21 - *
    2.22 - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    2.23 - * CA 95054 USA or visit www.sun.com if you need additional information or
    2.24 - * have any questions.
    2.25 - *
    2.26 - */
    2.27 -
    2.28 -void PtrQueue::handle_zero_index() {
    2.29 -  assert(0 == _index, "Precondition.");
    2.30 -  // This thread records the full buffer and allocates a new one (while
    2.31 -  // holding the lock if there is one).
    2.32 -  void** buf = _buf;
    2.33 -  _buf = qset()->allocate_buffer();
    2.34 -  _sz = qset()->buffer_size();
    2.35 -  _index = _sz;
    2.36 -  assert(0 <= _index && _index <= _sz, "Invariant.");
    2.37 -  if (buf != NULL) {
    2.38 -    if (_lock) {
    2.39 -      locking_enqueue_completed_buffer(buf);
    2.40 -    } else {
    2.41 -      qset()->enqueue_complete_buffer(buf);
    2.42 -    }
    2.43 -  }
    2.44 -}

mercurial