1 /* 2 * Copyright (c) 2018, 2020, Red Hat, 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "precompiled.hpp" 26 27 #include "gc/shenandoah/shenandoahHeap.inline.hpp" 28 #include "gc/shenandoah/shenandoahUtils.hpp" 29 #include "gc/shenandoah/shenandoahEvacOOMHandler.inline.hpp" 30 #include "runtime/os.hpp" 31 #include "runtime/thread.hpp" 32 33 const jint ShenandoahEvacOOMHandler::OOM_MARKER_MASK = 0x80000000; 34 35 ShenandoahEvacOOMHandler::ShenandoahEvacOOMHandler() : 36 _threads_in_evac(0) { 37 } 38 39 void ShenandoahEvacOOMHandler::wait_for_no_evac_threads() { 40 while ((Atomic::load_acquire(&_threads_in_evac) & ~OOM_MARKER_MASK) != 0) { 41 os::naked_short_sleep(1); 42 } 43 // At this point we are sure that no threads can evacuate anything. Raise 44 // the thread-local oom_during_evac flag to indicate that any attempt 45 // to evacuate should simply return the forwarding pointer instead (which is safe now). 46 ShenandoahThreadLocalData::set_oom_during_evac(Thread::current(), true); 47 } 48 49 void ShenandoahEvacOOMHandler::register_thread_to_protocol(Thread* thr) { 50 jint threads_in_evac = Atomic::load_acquire(&_threads_in_evac); 51 52 assert(!ShenandoahThreadLocalData::is_oom_during_evac(Thread::current()), "TL oom-during-evac must not be set"); 53 while (true) { 54 jint other = Atomic::cmpxchg(&_threads_in_evac, threads_in_evac, threads_in_evac + 1); 55 if (other == threads_in_evac) { 56 // Success: caller may safely enter evacuation 57 return; 58 } else { 59 // Failure: 60 // - if offender has OOM_MARKER_MASK, then loop until no more threads in evac 61 // - otherwise re-try CAS 62 if ((other & OOM_MARKER_MASK) != 0) { 63 wait_for_no_evac_threads(); 64 return; 65 } 66 threads_in_evac = other; 67 } 68 } 69 } 70 71 void ShenandoahEvacOOMHandler::unregister_thread_to_protocol(Thread* thr) { 72 if (!ShenandoahThreadLocalData::is_oom_during_evac(thr)) { 73 assert((Atomic::load_acquire(&_threads_in_evac) & ~OOM_MARKER_MASK) > 0, "sanity"); 74 // NOTE: It's ok to simply decrement, even with mask set, because unmasked value is positive. 75 Atomic::dec(&_threads_in_evac); 76 } else { 77 // If we get here, the current thread has already gone through the 78 // OOM-during-evac protocol and has thus either never entered or successfully left 79 // the evacuation region. Simply flip its TL oom-during-evac flag back off. 80 ShenandoahThreadLocalData::set_oom_during_evac(thr, false); 81 } 82 assert(!ShenandoahThreadLocalData::is_oom_during_evac(thr), "TL oom-during-evac must be turned off"); 83 } 84 85 void ShenandoahEvacOOMHandler::handle_out_of_memory_during_evacuation() { 86 assert(ShenandoahThreadLocalData::is_evac_allowed(Thread::current()), "sanity"); 87 assert(!ShenandoahThreadLocalData::is_oom_during_evac(Thread::current()), "TL oom-during-evac must not be set"); 88 89 jint threads_in_evac = Atomic::load_acquire(&_threads_in_evac); 90 while (true) { 91 jint other = Atomic::cmpxchg(&_threads_in_evac, threads_in_evac, (threads_in_evac - 1) | OOM_MARKER_MASK); 92 if (other == threads_in_evac) { 93 // Success: wait for other threads to get out of the protocol and return. 94 wait_for_no_evac_threads(); 95 return; 96 } else { 97 // Failure: try again with updated new value. 98 threads_in_evac = other; 99 } 100 } 101 } 102 103 void ShenandoahEvacOOMHandler::clear() { 104 assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "must be at a safepoint"); 105 assert((Atomic::load_acquire(&_threads_in_evac) & ~OOM_MARKER_MASK) == 0, "sanity"); 106 Atomic::release_store_fence(&_threads_in_evac, (jint)0); 107 }