| /* | |
| * Copyright (c) 2016, 2025, 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. | |
| */ | |
| #include "gc/z/zMarkingSMR.hpp" | |
| #include "gc/z/zMarkStack.inline.hpp" | |
| #include "gc/z/zValue.inline.hpp" | |
| #include "runtime/atomic.hpp" | |
| ZMarkingSMR::ZMarkingSMR() | |
| : _worker_states(), | |
| _expanded_recently() { | |
| } | |
| void ZMarkingSMR::free_node(ZMarkStackListNode* node) { | |
| // We use hazard pointers as an safe memory reclamation (SMR) technique, | |
| // for marking stacks. Each stripe has a lock-free stack of mark stacks. | |
| // When a GC thread (1) pops a mark stack from this lock-free stack, | |
| // there is a small window of time when the head has been read and we | |
| // are about to read its next pointer. It is then of great importance | |
| // that the node is not concurrently freed by another concurrent GC | |
| // thread (2), popping the same entry. In such an event, the memory | |
| // of the freed node could, for example become part of a separate | |
| // node, and potentially pushed onto a separate stripe, with a | |
| // different next pointer referring to a node of the other stripe. | |
| // When GC thread (1) then reads the next pointer of what it believed | |
| // to be the current head node of the first stripe, it actually read | |
| // a next pointer of a logically different node, pointing into the | |
| // other stripe. GC thread (2) could then pop the node from the second | |
| // mark stripe and re-insert it as the head of the first stripe. | |
| // Disaster eventually hits when GC thread (1) succeeds with its | |
| // CAS (ABA problem), switching the loaded head to the loaded next | |
| // pointer of the head. Due to the next pointer belonging to a logically | |
| // different node than the logical head, we can accidentally corrupt the | |
| // stack integrity. Using hazard pointers involves publishing what head | |
| // was observed by GC thread (1), so that GC thread (2) knows not to | |
| // free the node when popping it in this race. This prevents the racy | |
| // interactions from causing any such use-after-free problems. | |
| assert(Thread::current()->is_Worker_thread(), "must be a worker"); | |
| ZWorkerState* const local_state = _worker_states.addr(); | |
| ZArray<ZMarkStackListNode*>* const freeing = &local_state->_freeing; | |
| freeing->append(node); | |
| if (freeing->length() < (int)ZPerWorkerStorage::count() * 8) { | |
| return; | |
| } | |
| ZPerWorkerIterator<ZWorkerState> iter(&_worker_states); | |
| ZArray<ZMarkStackListNode*>* const scanned_hazards = &local_state->_scanned_hazards; | |
| for (ZWorkerState* remote_state; iter.next(&remote_state);) { | |
| ZMarkStackListNode* const hazard = Atomic::load(&remote_state->_hazard_ptr); | |
| if (hazard != nullptr) { | |
| scanned_hazards->append(hazard); | |
| } | |
| } | |
| int kept = 0; | |
| for (int i = 0; i < freeing->length(); ++i) { | |
| ZMarkStackListNode* node = freeing->at(i); | |
| freeing->at_put(i, nullptr); | |
| if (scanned_hazards->contains(node)) { | |
| // Keep | |
| freeing->at_put(kept++, node); | |
| } else { | |
| // Delete | |
| delete node; | |
| } | |
| } | |
| scanned_hazards->clear(); | |
| freeing->trunc_to(kept); | |
| } | |
| void ZMarkingSMR::free() { | |
| // Here it is free by definition to free mark stacks. | |
| ZPerWorkerIterator<ZWorkerState> iter(&_worker_states); | |
| for (ZWorkerState* worker_state; iter.next(&worker_state);) { | |
| ZArray<ZMarkStackListNode*>* const freeing = &worker_state->_freeing; | |
| for (ZMarkStackListNode* node: *freeing) { | |
| delete node; | |
| } | |
| freeing->clear(); | |
| } | |
| } | |
| ZMarkStackListNode* volatile* ZMarkingSMR::hazard_ptr() { | |
| return &_worker_states.addr()->_hazard_ptr; | |
| } |