hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp
void PtrQueue::handle_zero_index() {
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert(_index == 0, "Precondition.");
{- -------------------------------------------
(1) 今まで使用していたバッファがある場合 (= _buf が NULL ではない場合), 以下の処理を行う.
どの場合も, 条件によってはここでリターン.
* バッファの内容が有用ではない場合
(= PtrQueue::should_enqueue_buffer() (かサブクラスがオーバーライドしたもの) が false の場合):
特に何もすることはないので, 無条件でここでリターン.
(このケースは, 現状では SATB の処理で ConcurrentMarkThread の役に立たないバッファを弾くためにのみ使用されている.
なお, この場合は ObjPtrQueue::should_enqueue_buffer() 内で _index の値が戻され,
これまで使っていたバッファがそのまま再利用される.
See: ObjPtrQueue::should_enqueue_buffer())
* 有用で, かつ, ロックと関連づけられているバッファ(= _lock フィールドが true)の場合:
(See: DirtyCardQueueSet::_shared_dirty_card_queue, SATBMarkQueueSet::_shared_satb_queue)
PtrQueue::locking_enqueue_completed_buffer() を呼んで, バッファを enqueue する.
なお, 呼び出し後に _buf フィールドが NULL になっていない場合は, ここでリターン.
(NULL 以外の値になるのは, 他のスレッドと race した場合.
enqueue する瞬間にロックを一瞬開放する必要があるため,
そのままだと複数のスレッドが同じバッファを enqueue してしまう恐れがある
(See: PtrQueue::locking_enqueue_completed_buffer()).
それを防ぐため, ロックを解放する前に _buf を NULL にしている.
これにより, 重複した enquque は起こらなくなるが, 代わりにロックを手放している間に
他のスレッドが新しいバッファを確保して _buf にセットする可能性がある.
そのバッファには既にポインタが溜められているかもしれないので,
情報をロスしないようそのまま使用することにする.)
* 有用で, ロックと関連づけられていないバッファの場合:
PtrQueue::process_or_enqueue_complete_buffer() を呼んで, バッファを enqueue する.
返値が true であれば, ここでリターンする
(true になるのは, この場で処理してしまったため, すぐにバッファを再利用して問題ないケース).
---------------------------------------- -}
// This thread records the full buffer and allocates a new one (while
// holding the lock if there is one).
if (_buf != NULL) {
if (!should_enqueue_buffer()) {
assert(_index > 0, "the buffer can only be re-used if it's not full");
return;
}
if (_lock) {
assert(_lock->owned_by_self(), "Required.");
// The current PtrQ may be the shared dirty card queue and
// may be being manipulated by more than one worker thread
// during a pause. Since the enqueuing of the completed
// buffer unlocks the Shared_DirtyCardQ_lock more than one
// worker thread can 'race' on reading the shared queue attributes
// (_buf and _index) and multiple threads can call into this
// routine for the same buffer. This will cause the completed
// buffer to be added to the CBL multiple times.
// We "claim" the current buffer by caching value of _buf in
// a local and clearing the field while holding _lock. When
// _lock is released (while enqueueing the completed buffer)
// the thread that acquires _lock will skip this code,
// preventing the subsequent the multiple enqueue, and
// install a newly allocated buffer below.
void** buf = _buf; // local pointer to completed buffer
_buf = NULL; // clear shared _buf field
locking_enqueue_completed_buffer(buf); // enqueue completed buffer
// While the current thread was enqueuing the buffer another thread
// may have a allocated a new buffer and inserted it into this pointer
// queue. If that happens then we just return so that the current
// thread doesn't overwrite the buffer allocated by the other thread
// and potentially losing some dirtied cards.
if (_buf != NULL) return;
} else {
if (qset()->process_or_enqueue_complete_buffer(_buf)) {
// Recycle the buffer. No allocation.
_sz = qset()->buffer_size();
_index = _sz;
return;
}
}
}
{- -------------------------------------------
(1) PtrQueueSet::allocate_buffer() を呼んで, 新しいバッファを確保する.
(ついでに _sz フィールドや _index フィールドもリセットし,
そのバッファの大きさをセットしておく.)
---------------------------------------- -}
// Reallocate the buffer
_buf = qset()->allocate_buffer();
_sz = qset()->buffer_size();
_index = _sz;
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert(0 <= _index && _index <= _sz, "Invariant.");
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.