src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp

Mon, 03 Aug 2009 12:59:30 -0700

author
johnc
date
Mon, 03 Aug 2009 12:59:30 -0700
changeset 1324
15c5903cf9e1
parent 1320
7f807f55161a
child 1525
fa357420e7d2
permissions
-rw-r--r--

6865703: G1: Parallelize hot card cache cleanup
Summary: Have the GC worker threads clear the hot card cache in parallel by having each worker thread claim a chunk of the card cache and process the cards in that chunk. The size of the chunks that each thread will claim is determined at VM initialization from the size of the card cache and the number of worker threads.
Reviewed-by: jmasa, tonyp

     1 /*
     2  * Copyright 2001-2009 Sun Microsystems, Inc.  All Rights Reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     8  *
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    12  * version 2 for more details (a copy is included in the LICENSE file that
    13  * accompanied this code).
    14  *
    15  * You should have received a copy of the GNU General Public License version
    16  * 2 along with this work; if not, write to the Free Software Foundation,
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    18  *
    19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    20  * CA 95054 USA or visit www.sun.com if you need additional information or
    21  * have any questions.
    22  *
    23  */
    25 # include "incls/_precompiled.incl"
    26 # include "incls/_dirtyCardQueue.cpp.incl"
    28 bool DirtyCardQueue::apply_closure(CardTableEntryClosure* cl,
    29                                    bool consume,
    30                                    size_t worker_i) {
    31   bool res = true;
    32   if (_buf != NULL) {
    33     res = apply_closure_to_buffer(cl, _buf, _index, _sz,
    34                                   consume,
    35                                   (int) worker_i);
    36     if (res && consume) _index = _sz;
    37   }
    38   return res;
    39 }
    41 bool DirtyCardQueue::apply_closure_to_buffer(CardTableEntryClosure* cl,
    42                                              void** buf,
    43                                              size_t index, size_t sz,
    44                                              bool consume,
    45                                              int worker_i) {
    46   if (cl == NULL) return true;
    47   for (size_t i = index; i < sz; i += oopSize) {
    48     int ind = byte_index_to_index((int)i);
    49     jbyte* card_ptr = (jbyte*)buf[ind];
    50     if (card_ptr != NULL) {
    51       // Set the entry to null, so we don't do it again (via the test
    52       // above) if we reconsider this buffer.
    53       if (consume) buf[ind] = NULL;
    54       if (!cl->do_card_ptr(card_ptr, worker_i)) return false;
    55     }
    56   }
    57   return true;
    58 }
    60 #ifdef _MSC_VER // the use of 'this' below gets a warning, make it go away
    61 #pragma warning( disable:4355 ) // 'this' : used in base member initializer list
    62 #endif // _MSC_VER
    64 DirtyCardQueueSet::DirtyCardQueueSet() :
    65   PtrQueueSet(true /*notify_when_complete*/),
    66   _closure(NULL),
    67   _shared_dirty_card_queue(this, true /*perm*/),
    68   _free_ids(NULL),
    69   _processed_buffers_mut(0), _processed_buffers_rs_thread(0)
    70 {
    71   _all_active = true;
    72 }
    74 // Determines how many mutator threads can process the buffers in parallel.
    75 size_t DirtyCardQueueSet::num_par_ids() {
    76   return os::processor_count();
    77 }
    79 void DirtyCardQueueSet::initialize(Monitor* cbl_mon, Mutex* fl_lock,
    80                                    int max_completed_queue,
    81                                    Mutex* lock, PtrQueueSet* fl_owner) {
    82   PtrQueueSet::initialize(cbl_mon, fl_lock, max_completed_queue, fl_owner);
    83   set_buffer_size(G1UpdateBufferSize);
    84   set_process_completed_threshold(G1UpdateBufferQueueProcessingThreshold);
    86   _shared_dirty_card_queue.set_lock(lock);
    87   _free_ids = new FreeIdSet((int) num_par_ids(), _cbl_mon);
    88 }
    90 void DirtyCardQueueSet::handle_zero_index_for_thread(JavaThread* t) {
    91   t->dirty_card_queue().handle_zero_index();
    92 }
    94 void DirtyCardQueueSet::set_closure(CardTableEntryClosure* closure) {
    95   _closure = closure;
    96 }
    98 void DirtyCardQueueSet::iterate_closure_all_threads(bool consume,
    99                                                     size_t worker_i) {
   100   assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
   101   for(JavaThread* t = Threads::first(); t; t = t->next()) {
   102     bool b = t->dirty_card_queue().apply_closure(_closure, consume);
   103     guarantee(b, "Should not be interrupted.");
   104   }
   105   bool b = shared_dirty_card_queue()->apply_closure(_closure,
   106                                                     consume,
   107                                                     worker_i);
   108   guarantee(b, "Should not be interrupted.");
   109 }
   111 bool DirtyCardQueueSet::mut_process_buffer(void** buf) {
   113   // Used to determine if we had already claimed a par_id
   114   // before entering this method.
   115   bool already_claimed = false;
   117   // We grab the current JavaThread.
   118   JavaThread* thread = JavaThread::current();
   120   // We get the the number of any par_id that this thread
   121   // might have already claimed.
   122   int worker_i = thread->get_claimed_par_id();
   124   // If worker_i is not -1 then the thread has already claimed
   125   // a par_id. We make note of it using the already_claimed value
   126   if (worker_i != -1) {
   127     already_claimed = true;
   128   } else {
   130     // Otherwise we need to claim a par id
   131     worker_i = _free_ids->claim_par_id();
   133     // And store the par_id value in the thread
   134     thread->set_claimed_par_id(worker_i);
   135   }
   137   bool b = false;
   138   if (worker_i != -1) {
   139     b = DirtyCardQueue::apply_closure_to_buffer(_closure, buf, 0,
   140                                                 _sz, true, worker_i);
   141     if (b) Atomic::inc(&_processed_buffers_mut);
   143     // If we had not claimed an id before entering the method
   144     // then we must release the id.
   145     if (!already_claimed) {
   147       // we release the id
   148       _free_ids->release_par_id(worker_i);
   150       // and set the claimed_id in the thread to -1
   151       thread->set_claimed_par_id(-1);
   152     }
   153   }
   154   return b;
   155 }
   157 DirtyCardQueueSet::CompletedBufferNode*
   158 DirtyCardQueueSet::get_completed_buffer_lock(int stop_at) {
   159   CompletedBufferNode* nd = NULL;
   160   MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag);
   162   if ((int)_n_completed_buffers <= stop_at) {
   163     _process_completed = false;
   164     return NULL;
   165   }
   167   if (_completed_buffers_head != NULL) {
   168     nd = _completed_buffers_head;
   169     _completed_buffers_head = nd->next;
   170     if (_completed_buffers_head == NULL)
   171       _completed_buffers_tail = NULL;
   172     _n_completed_buffers--;
   173   }
   174   debug_only(assert_completed_buffer_list_len_correct_locked());
   175   return nd;
   176 }
   178 // We only do this in contexts where there is no concurrent enqueueing.
   179 DirtyCardQueueSet::CompletedBufferNode*
   180 DirtyCardQueueSet::get_completed_buffer_CAS() {
   181   CompletedBufferNode* nd = _completed_buffers_head;
   183   while (nd != NULL) {
   184     CompletedBufferNode* next = nd->next;
   185     CompletedBufferNode* result =
   186       (CompletedBufferNode*)Atomic::cmpxchg_ptr(next,
   187                                                 &_completed_buffers_head,
   188                                                 nd);
   189     if (result == nd) {
   190       return result;
   191     } else {
   192       nd = _completed_buffers_head;
   193     }
   194   }
   195   assert(_completed_buffers_head == NULL, "Loop post");
   196   _completed_buffers_tail = NULL;
   197   return NULL;
   198 }
   200 bool DirtyCardQueueSet::
   201 apply_closure_to_completed_buffer_helper(int worker_i,
   202                                          CompletedBufferNode* nd) {
   203   if (nd != NULL) {
   204     bool b =
   205       DirtyCardQueue::apply_closure_to_buffer(_closure, nd->buf,
   206                                               nd->index, _sz,
   207                                               true, worker_i);
   208     void** buf = nd->buf;
   209     size_t index = nd->index;
   210     delete nd;
   211     if (b) {
   212       deallocate_buffer(buf);
   213       return true;  // In normal case, go on to next buffer.
   214     } else {
   215       enqueue_complete_buffer(buf, index, true);
   216       return false;
   217     }
   218   } else {
   219     return false;
   220   }
   221 }
   223 bool DirtyCardQueueSet::apply_closure_to_completed_buffer(int worker_i,
   224                                                           int stop_at,
   225                                                           bool with_CAS)
   226 {
   227   CompletedBufferNode* nd = NULL;
   228   if (with_CAS) {
   229     guarantee(stop_at == 0, "Precondition");
   230     nd = get_completed_buffer_CAS();
   231   } else {
   232     nd = get_completed_buffer_lock(stop_at);
   233   }
   234   bool res = apply_closure_to_completed_buffer_helper(worker_i, nd);
   235   if (res) Atomic::inc(&_processed_buffers_rs_thread);
   236   return res;
   237 }
   239 void DirtyCardQueueSet::apply_closure_to_all_completed_buffers() {
   240   CompletedBufferNode* nd = _completed_buffers_head;
   241   while (nd != NULL) {
   242     bool b =
   243       DirtyCardQueue::apply_closure_to_buffer(_closure, nd->buf, 0, _sz,
   244                                               false);
   245     guarantee(b, "Should not stop early.");
   246     nd = nd->next;
   247   }
   248 }
   250 void DirtyCardQueueSet::abandon_logs() {
   251   assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
   252   CompletedBufferNode* buffers_to_delete = NULL;
   253   {
   254     MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag);
   255     while (_completed_buffers_head != NULL) {
   256       CompletedBufferNode* nd = _completed_buffers_head;
   257       _completed_buffers_head = nd->next;
   258       nd->next = buffers_to_delete;
   259       buffers_to_delete = nd;
   260     }
   261     _n_completed_buffers = 0;
   262     _completed_buffers_tail = NULL;
   263     debug_only(assert_completed_buffer_list_len_correct_locked());
   264   }
   265   while (buffers_to_delete != NULL) {
   266     CompletedBufferNode* nd = buffers_to_delete;
   267     buffers_to_delete = nd->next;
   268     deallocate_buffer(nd->buf);
   269     delete nd;
   270   }
   271   // Since abandon is done only at safepoints, we can safely manipulate
   272   // these queues.
   273   for (JavaThread* t = Threads::first(); t; t = t->next()) {
   274     t->dirty_card_queue().reset();
   275   }
   276   shared_dirty_card_queue()->reset();
   277 }
   280 void DirtyCardQueueSet::concatenate_logs() {
   281   // Iterate over all the threads, if we find a partial log add it to
   282   // the global list of logs.  Temporarily turn off the limit on the number
   283   // of outstanding buffers.
   284   int save_max_completed_queue = _max_completed_queue;
   285   _max_completed_queue = max_jint;
   286   assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
   287   for (JavaThread* t = Threads::first(); t; t = t->next()) {
   288     DirtyCardQueue& dcq = t->dirty_card_queue();
   289     if (dcq.size() != 0) {
   290       void **buf = t->dirty_card_queue().get_buf();
   291       // We must NULL out the unused entries, then enqueue.
   292       for (size_t i = 0; i < t->dirty_card_queue().get_index(); i += oopSize) {
   293         buf[PtrQueue::byte_index_to_index((int)i)] = NULL;
   294       }
   295       enqueue_complete_buffer(dcq.get_buf(), dcq.get_index());
   296       dcq.reinitialize();
   297     }
   298   }
   299   if (_shared_dirty_card_queue.size() != 0) {
   300     enqueue_complete_buffer(_shared_dirty_card_queue.get_buf(),
   301                             _shared_dirty_card_queue.get_index());
   302     _shared_dirty_card_queue.reinitialize();
   303   }
   304   // Restore the completed buffer queue limit.
   305   _max_completed_queue = save_max_completed_queue;
   306 }

mercurial