| /* | |
| * Copyright (c) 2001, 2024, 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. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| * | |
| */ | |
| #ifndef SHARE_GC_SHARED_SATBMARKQUEUE_HPP | |
| #define SHARE_GC_SHARED_SATBMARKQUEUE_HPP | |
| #include "gc/shared/ptrQueue.hpp" | |
| #include "memory/allocation.hpp" | |
| #include "memory/padded.hpp" | |
| #include "oops/oopsHierarchy.hpp" | |
| class Thread; | |
| class Monitor; | |
| class SATBMarkQueueSet; | |
| // Base class for processing the contents of a SATB buffer. | |
| class SATBBufferClosure : public StackObj { | |
| protected: | |
| ~SATBBufferClosure() { } | |
| public: | |
| // Process the SATB entries in the designated buffer range. | |
| virtual void do_buffer(void** buffer, size_t size) = 0; | |
| }; | |
| // A PtrQueue whose elements are (possibly stale) pointers to object heads. | |
| class SATBMarkQueue: public PtrQueue { | |
| friend class VMStructs; | |
| friend class SATBMarkQueueSet; | |
| private: | |
| // Per-queue (so thread-local) cache of the SATBMarkQueueSet's | |
| // active state, to support inline barriers in compiled code. | |
| bool _active; | |
| public: | |
| SATBMarkQueue(SATBMarkQueueSet* qset); | |
| bool is_active() const { return _active; } | |
| void set_active(bool value) { _active = value; } | |
| #ifndef PRODUCT | |
| // Helpful for debugging | |
| void print(const char* name); | |
| #endif // PRODUCT | |
| // Compiler support. | |
| static ByteSize byte_offset_of_index() { | |
| return PtrQueue::byte_offset_of_index<SATBMarkQueue>(); | |
| } | |
| using PtrQueue::byte_width_of_index; | |
| static ByteSize byte_offset_of_buf() { | |
| return PtrQueue::byte_offset_of_buf<SATBMarkQueue>(); | |
| } | |
| using PtrQueue::byte_width_of_buf; | |
| static ByteSize byte_offset_of_active() { | |
| return byte_offset_of(SATBMarkQueue, _active); | |
| } | |
| static ByteSize byte_width_of_active() { return in_ByteSize(sizeof(bool)); } | |
| }; | |
| class SATBMarkQueueSet: public PtrQueueSet { | |
| DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, 0); | |
| PaddedEnd<BufferNode::Stack> _list; | |
| volatile size_t _count_and_process_flag; | |
| // These are rarely (if ever) changed, so same cache line as count. | |
| size_t _process_completed_buffers_threshold; | |
| size_t _buffer_enqueue_threshold; | |
| // SATB is only active during marking. Enqueuing is only done when active. | |
| bool _all_active; | |
| DEFINE_PAD_MINUS_SIZE(2, DEFAULT_PADDING_SIZE, 4 * sizeof(size_t)); | |
| BufferNode* get_completed_buffer(); | |
| void abandon_completed_buffers(); | |
| #ifdef ASSERT | |
| void dump_active_states(bool expected_active); | |
| void verify_active_states(bool expected_active); | |
| #endif // ASSERT | |
| protected: | |
| SATBMarkQueueSet(BufferNode::Allocator* allocator); | |
| ~SATBMarkQueueSet(); | |
| void handle_zero_index(SATBMarkQueue& queue); | |
| // Return true if the queue's buffer should be enqueued, even if not full. | |
| // The default method uses the buffer enqueue threshold. | |
| bool should_enqueue_buffer(SATBMarkQueue& queue); | |
| template<typename Filter> | |
| void apply_filter(Filter filter, SATBMarkQueue& queue); | |
| public: | |
| virtual SATBMarkQueue& satb_queue_for_thread(Thread* const t) const = 0; | |
| bool is_active() const { return _all_active; } | |
| // Apply "set_active(active)" to all SATB queues in the set. It should be | |
| // called only with the world stopped. The method will assert that the | |
| // SATB queues of all threads it visits, as well as the SATB queue | |
| // set itself, has an active value same as expected_active. | |
| void set_active_all_threads(bool active, bool expected_active); | |
| void set_process_completed_buffers_threshold(size_t value); | |
| size_t buffer_enqueue_threshold() const { return _buffer_enqueue_threshold; } | |
| void set_buffer_enqueue_threshold_percentage(uint value); | |
| // If there exists some completed buffer, pop and process it, and | |
| // return true. Otherwise return false. Processing a buffer | |
| // consists of applying the closure to the active range of the | |
| // buffer; the leading entries may be excluded due to filtering. | |
| bool apply_closure_to_completed_buffer(SATBBufferClosure* cl); | |
| void flush_queue(SATBMarkQueue& queue); | |
| // Add obj to queue. This qset and the queue must be active. | |
| void enqueue_known_active(SATBMarkQueue& queue, oop obj); | |
| virtual void filter(SATBMarkQueue& queue) = 0; | |
| virtual void enqueue_completed_buffer(BufferNode* node); | |
| // The number of buffers in the list. Racy and not updated atomically | |
| // with the set of completed buffers. | |
| size_t completed_buffers_num() const { | |
| return _count_and_process_flag >> 1; | |
| } | |
| // Return true if completed buffers should be processed. | |
| bool process_completed_buffers() const { | |
| return (_count_and_process_flag & 1) != 0; | |
| } | |
| #ifndef PRODUCT | |
| // Helpful for debugging | |
| void print_all(const char* msg); | |
| #endif // PRODUCT | |
| // If a marking is being abandoned, reset any unprocessed log buffers. | |
| void abandon_partial_marking(); | |
| }; | |
| // Removes entries from queue's buffer that are no longer needed, as | |
| // determined by filter. If e is a void* entry in queue's buffer, | |
| // filter_out(e) must be a valid expression whose value is convertible | |
| // to bool. Entries are removed (filtered out) if the result is true, | |
| // retained if false. | |
| template<typename Filter> | |
| inline void SATBMarkQueueSet::apply_filter(Filter filter_out, SATBMarkQueue& queue) { | |
| void** buf = queue.buffer(); | |
| if (buf == nullptr) { | |
| // Nothing to do, and avoid pointer arithmetic on nullptr below. | |
| return; | |
| } | |
| // Two-fingered compaction toward the end. | |
| void** src = buf + queue.index(); | |
| void** dst = buf + queue.current_capacity(); | |
| assert(src <= dst, "invariant"); | |
| for ( ; src < dst; ++src) { | |
| // Search low to high for an entry to keep. | |
| void* entry = *src; | |
| if (!filter_out(entry)) { | |
| // Found keeper. Search high to low for an entry to discard. | |
| while (src < --dst) { | |
| if (filter_out(*dst)) { | |
| *dst = entry; // Replace discard with keeper. | |
| break; | |
| } | |
| } | |
| // If discard search failed (src == dst), the outer loop will also end. | |
| } | |
| } | |
| // dst points to the lowest retained entry, or the end of the buffer | |
| // if all the entries were filtered out. | |
| queue.set_index(dst - buf); | |
| } | |
| #endif // SHARE_GC_SHARED_SATBMARKQUEUE_HPP |