< prev index next >
src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp
Print this page
rev 57716 : imported patch remove_cbl_mon
rev 57717 : [mq]: helper_classes
*** 1,7 ****
/*
! * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
--- 1,7 ----
/*
! * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*** 27,41 ****
#include "gc/g1/g1BufferNodeList.hpp"
#include "gc/g1/g1FreeIdSet.hpp"
#include "gc/shared/ptrQueue.hpp"
#include "memory/allocation.hpp"
class G1DirtyCardQueueSet;
class G1RedirtyCardsQueueSet;
class Thread;
- class Monitor;
// A ptrQueue whose elements are "oops", pointers to object heads.
class G1DirtyCardQueue: public PtrQueue {
protected:
virtual void handle_completed_buffer();
--- 27,42 ----
#include "gc/g1/g1BufferNodeList.hpp"
#include "gc/g1/g1FreeIdSet.hpp"
#include "gc/shared/ptrQueue.hpp"
#include "memory/allocation.hpp"
+ #include "memory/padded.hpp"
+ class G1ConcurrentRefineThread;
class G1DirtyCardQueueSet;
class G1RedirtyCardsQueueSet;
class Thread;
// A ptrQueue whose elements are "oops", pointers to object heads.
class G1DirtyCardQueue: public PtrQueue {
protected:
virtual void handle_completed_buffer();
*** 64,82 ****
using PtrQueue::byte_width_of_buf;
};
class G1DirtyCardQueueSet: public PtrQueueSet {
! Monitor* _cbl_mon; // Protects the list and count members.
! BufferNode* _completed_buffers_head;
! BufferNode* _completed_buffers_tail;
!
! // Number of actual cards in the list of completed buffers.
volatile size_t _num_cards;
size_t _process_cards_threshold;
! volatile bool _process_completed_buffers;
void abandon_completed_buffers();
// Refine the cards in "node" from its index to buffer_size.
// Stops processing if SuspendibleThreadSet::should_yield() is true.
--- 65,246 ----
using PtrQueue::byte_width_of_buf;
};
class G1DirtyCardQueueSet: public PtrQueueSet {
! // Head and tail of a list of BufferNodes, linked through their next()
! // fields. Similar to G1BufferNodeList, but without the _entry_count.
! struct HeadTail {
! BufferNode* _head;
! BufferNode* _tail;
! HeadTail() : _head(NULL), _tail(NULL) {}
! HeadTail(BufferNode* head, BufferNode* tail) : _head(head), _tail(tail) {}
! };
!
! // A lock-free FIFO of BufferNodes, linked through their next() fields.
! // This class has a restriction that pop() cannot return the last buffer
! // in the queue, or what was the last buffer for a concurrent push/append
! // operation. It is expected that there will be a later push/append that
! // will make that buffer available to a future pop(), or there will
! // eventually be a complete transfer via take_all().
! class Queue {
! BufferNode* volatile _head;
! DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(BufferNode*));
! BufferNode* volatile _tail;
! DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(BufferNode*));
!
! NONCOPYABLE(Queue);
!
! public:
! Queue() : _head(NULL), _tail(NULL) {}
! DEBUG_ONLY(~Queue();)
!
! // Return the first buffer in the queue.
! // Thread-safe, but the result may change immediately.
! BufferNode* top() const;
!
! // Thread-safe add the buffer to the end of the queue.
! void push(BufferNode& node) { append(node, node); }
!
! // Thread-safe add the buffers from first to last to the end of the queue.
! void append(BufferNode& first, BufferNode& last);
!
! // Thread-safe attempt to remove and return the first buffer in the queue.
! // Returns NULL if the queue is empty, or if only one buffer is found.
! // Uses GlobalCounter critical sections to address the ABA problem; this
! // works with the buffer allocator's use of GlobalCounter synchronization.
! BufferNode* pop();
!
! // Take all the buffers from the queue, leaving the queue empty.
! // Not thread-safe.
! HeadTail take_all();
! };
!
! // Concurrent refinement may stop processing in the middle of a buffer if
! // there is a pending safepoint, to avoid long delays to safepoint. A
! // partially processed buffer needs to be recorded for processing by the
! // safepoint if it's a GC safepoint; otherwise it needs to be recorded for
! // further concurrent refinement work after the safepoint. But if the
! // buffer was obtained from the completed buffer queue then it can't simply
! // be added back to the queue, as that would introduce a new source of ABA
! // for the queue.
! //
! // The PausedBuffer object is used to record such buffers for the upcoming
! // safepoint, and provides access to the buffers recorded for previous
! // safepoints. Before obtaining a buffer from the completed buffers queue,
! // we first transfer any buffers from previous safepoints to the queue.
! // This is ABA-safe because threads cannot be in the midst of a queue pop
! // across a safepoint.
! //
! // The paused buffers are conceptually an extension of the completed buffers
! // queue, and operations which need to deal with all of the queued buffers
! // (such as concatenate_logs) also need to deal with any paused buffers. In
! // general, if a safepoint performs a GC then the paused buffers will be
! // processed as part of it, and there won't be any paused buffers after a
! // GC safepoint.
! class PausedBuffers {
! class PausedList : public CHeapObj<mtGC> {
! BufferNode* volatile _head;
! BufferNode* _tail;
! size_t _safepoint_id;
!
! NONCOPYABLE(PausedList);
!
! public:
! PausedList();
! DEBUG_ONLY(~PausedList();)
!
! // Return true if this list was created to hold buffers for the
! // next safepoint.
! // precondition: not at safepoint.
! bool is_next() const;
!
! // Thread-safe add the buffer to the list.
! // precondition: not at safepoint.
! // precondition: is_next().
! void add(BufferNode* node);
!
! // Take all the buffers from the list. Not thread-safe.
! HeadTail take();
! };
!
! // The most recently created list, which might be for either the next or
! // a previous safepoint, or might be NULL if the next list hasn't been
! // created yet. We only need one list because of the requirement that
! // threads calling add() must first ensure there are no paused buffers
! // from a previous safepoint. There might be many list instances existing
! // at the same time though; there can be many threads competing to create
! // and install the next list, and meanwhile there can be a thread dealing
! // with the previous list.
! PausedList* volatile _plist;
! DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(PausedList*));
!
! NONCOPYABLE(PausedBuffers);
!
! public:
! PausedBuffers();
! DEBUG_ONLY(~PausedBuffers();)
!
! // Test whether there are any paused lists.
! // Thread-safe, but the answer may change immediately.
! bool empty() const;
!
! // Thread-safe add the buffer to paused list for next safepoint.
! // precondition: not at safepoint.
! // precondition: does not have paused buffers from a previous safepoint.
! void add(BufferNode* node);
!
! // Thread-safe take all paused buffers for previous safepoints.
! // precondition: not at safepoint.
! HeadTail take_previous();
!
! // Take all the paused buffers.
! // precondition: at safepoint.
! HeadTail take_all();
! };
!
! // The primary refinement thread, for activation when the processing
! // threshold is reached. NULL if there aren't any refinement threads.
! G1ConcurrentRefineThread* _primary_refinement_thread;
! DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(G1ConcurrentRefineThread*));
! // Upper bound on the number of cards in the completed and paused buffers.
volatile size_t _num_cards;
+ DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(size_t));
+ // Buffers ready for refinement.
+ Queue _completed; // Has inner padding, including trailer.
+ // Buffers for which refinement is temporarily paused.
+ PausedBuffers _paused; // Has inner padding, including trailer.
+ G1FreeIdSet _free_ids;
+
+ // Activation threshold for the primary refinement thread.
size_t _process_cards_threshold;
!
! // If the queue contains more cards than configured here, the
! // mutator must start doing some of the concurrent refinement work.
! size_t _max_cards;
! size_t _max_cards_padding;
! static const size_t MaxCardsUnlimited = SIZE_MAX;
!
! // Array of cumulative dirty cards refined by mutator threads.
! // Array has an entry per id in _free_ids.
! size_t* _mutator_refined_cards_counters;
!
! // Verify _num_cards == sum of cards in the completed queue.
! void verify_num_cards() const NOT_DEBUG_RETURN;
!
! // Thread-safe add a buffer to paused list for next safepoint.
! // precondition: not at safepoint.
! // precondition: does not have paused buffers from a previous safepoint.
! void record_paused_buffer(BufferNode* node);
! void enqueue_paused_buffers_aux(const HeadTail& paused);
! // Thread-safe transfer paused buffers for previous safepoints to the queue.
! // precondition: not at safepoint.
! void enqueue_previous_paused_buffers();
! // Transfer all paused buffers to the queue.
! // precondition: at safepoint.
! void enqueue_all_paused_buffers();
void abandon_completed_buffers();
// Refine the cards in "node" from its index to buffer_size.
// Stops processing if SuspendibleThreadSet::should_yield() is true.
*** 88,113 ****
// processed and removed from the buffer.
bool refine_buffer(BufferNode* node, uint worker_id, size_t* total_refined_cards);
bool mut_process_buffer(BufferNode* node);
! // If the queue contains more cards than configured here, the
! // mutator must start doing some of the concurrent refinement work.
! size_t _max_cards;
! size_t _max_cards_padding;
! static const size_t MaxCardsUnlimited = SIZE_MAX;
!
! G1FreeIdSet _free_ids;
!
! // Array of cumulative dirty cards refined by mutator threads.
! // Array has an entry per id in _free_ids.
! size_t* _mutator_refined_cards_counters;
public:
! G1DirtyCardQueueSet(Monitor* cbl_mon, BufferNode::Allocator* allocator);
~G1DirtyCardQueueSet();
// The number of parallel ids that can be claimed to allow collector or
// mutator threads to do card-processing work.
static uint num_par_ids();
static void handle_zero_index_for_thread(Thread* t);
--- 252,273 ----
// processed and removed from the buffer.
bool refine_buffer(BufferNode* node, uint worker_id, size_t* total_refined_cards);
bool mut_process_buffer(BufferNode* node);
! // If the number of completed buffers is > stop_at, then remove and
! // return a completed buffer from the list. Otherwise, return NULL.
! BufferNode* get_completed_buffer(size_t stop_at = 0);
public:
! G1DirtyCardQueueSet(BufferNode::Allocator* allocator);
~G1DirtyCardQueueSet();
+ void set_primary_refinement_thread(G1ConcurrentRefineThread* thread) {
+ _primary_refinement_thread = thread;
+ }
+
// The number of parallel ids that can be claimed to allow collector or
// mutator threads to do card-processing work.
static uint num_par_ids();
static void handle_zero_index_for_thread(Thread* t);
*** 117,140 ****
// it can be reused in place.
bool process_or_enqueue_completed_buffer(BufferNode* node);
virtual void enqueue_completed_buffer(BufferNode* node);
! // If the number of completed buffers is > stop_at, then remove and
! // return a completed buffer from the list. Otherwise, return NULL.
! BufferNode* get_completed_buffer(size_t stop_at = 0);
!
! // The number of cards in completed buffers. Read without synchronization.
size_t num_cards() const { return _num_cards; }
- // Verify that _num_cards is equal to the sum of actual cards
- // in the completed buffers.
- void verify_num_cards() const NOT_DEBUG_RETURN;
-
- bool process_completed_buffers() { return _process_completed_buffers; }
- void set_process_completed_buffers(bool x) { _process_completed_buffers = x; }
-
// Get/Set the number of cards that triggers log processing.
// Log processing should be done when the number of cards exceeds the
// threshold.
void set_process_cards_threshold(size_t sz) {
_process_cards_threshold = sz;
--- 277,291 ----
// it can be reused in place.
bool process_or_enqueue_completed_buffer(BufferNode* node);
virtual void enqueue_completed_buffer(BufferNode* node);
! // Upper bound on the number of cards currently in in this queue set.
! // Read without synchronization. The value may be high because there
! // is a concurrent modification of the set of buffers.
size_t num_cards() const { return _num_cards; }
// Get/Set the number of cards that triggers log processing.
// Log processing should be done when the number of cards exceeds the
// threshold.
void set_process_cards_threshold(size_t sz) {
_process_cards_threshold = sz;
*** 154,165 ****
// If there are more than stop_at cards in the completed buffers, pop
// a buffer, refine its contents, and return true. Otherwise return
// false.
//
// Stops processing a buffer if SuspendibleThreadSet::should_yield(),
! // returning the incompletely processed buffer to the completed buffer
! // list, for later processing of the remainder.
//
// Increments *total_refined_cards by the number of cards processed and
// removed from the buffer.
bool refine_completed_buffer_concurrently(uint worker_id,
size_t stop_at,
--- 305,316 ----
// If there are more than stop_at cards in the completed buffers, pop
// a buffer, refine its contents, and return true. Otherwise return
// false.
//
// Stops processing a buffer if SuspendibleThreadSet::should_yield(),
! // recording the incompletely processed buffer for later processing of
! // the remainder.
//
// Increments *total_refined_cards by the number of cards processed and
// removed from the buffer.
bool refine_completed_buffer_concurrently(uint worker_id,
size_t stop_at,
< prev index next >