1 /* 2 * Copyright (c) 2018, 2019, 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.hpp" 30 #include "gc/shenandoah/shenandoahThreadLocalData.hpp" 31 #include "runtime/atomic.hpp" 32 #include "runtime/os.hpp" 33 #include "runtime/thread.hpp" 34 35 const jint ShenandoahEvacOOMHandler::OOM_MARKER_MASK = 0x80000000; 36 37 ShenandoahEvacOOMHandler::ShenandoahEvacOOMHandler() : 38 _threads_in_evac(0) { 39 } 40 41 void ShenandoahEvacOOMHandler::wait_for_no_evac_threads() { 42 while ((Atomic::load_acquire(&_threads_in_evac) & ~OOM_MARKER_MASK) != 0) { 43 os::naked_short_sleep(1); 44 } 45 // At this point we are sure that no threads can evacuate anything. Raise 46 // the thread-local oom_during_evac flag to indicate that any attempt 47 // to evacuate should simply return the forwarding pointer instead (which is safe now). 48 ShenandoahThreadLocalData::set_oom_during_evac(Thread::current(), true); 49 } 50 51 void ShenandoahEvacOOMHandler::enter_evacuation() { 52 jint threads_in_evac = Atomic::load_acquire(&_threads_in_evac); 53 54 Thread* const thr = Thread::current(); 55 uint8_t level = ShenandoahThreadLocalData::push_evac_oom_scope(thr); 56 if ((threads_in_evac & OOM_MARKER_MASK) != 0) { 57 wait_for_no_evac_threads(); 58 return; 59 } 60 61 // Nesting case, this thread already registered 62 if (level > 0) { 63 return; 64 } 65 66 assert(!ShenandoahThreadLocalData::is_oom_during_evac(Thread::current()), "TL oom-during-evac must not be set"); 67 while (true) { 68 jint other = Atomic::cmpxchg(&_threads_in_evac, threads_in_evac, threads_in_evac + 1); 69 if (other == threads_in_evac) { 70 // Success: caller may safely enter evacuation 71 return; 72 } else { 73 // Failure: 74 // - if offender has OOM_MARKER_MASK, then loop until no more threads in evac 75 // - otherwise re-try CAS 76 if ((other & OOM_MARKER_MASK) != 0) { 77 wait_for_no_evac_threads(); 78 return; 79 } 80 threads_in_evac = other; 81 } 82 } 83 } 84 85 void ShenandoahEvacOOMHandler::leave_evacuation() { 86 Thread* const thr = Thread::current(); 87 uint8_t level = ShenandoahThreadLocalData::pop_evac_oom_scope(thr); 88 // Not top level, just return 89 if (level > 1) { 90 return; 91 } 92 93 if (!ShenandoahThreadLocalData::is_oom_during_evac(thr)) { 94 assert((Atomic::load_acquire(&_threads_in_evac) & ~OOM_MARKER_MASK) > 0, "sanity"); 95 // NOTE: It's ok to simply decrement, even with mask set, because unmasked value is positive. 96 Atomic::dec(&_threads_in_evac); 97 } else { 98 // If we get here, the current thread has already gone through the 99 // OOM-during-evac protocol and has thus either never entered or successfully left 100 // the evacuation region. Simply flip its TL oom-during-evac flag back off. 101 ShenandoahThreadLocalData::set_oom_during_evac(thr, false); 102 } 103 assert(!ShenandoahThreadLocalData::is_oom_during_evac(thr), "TL oom-during-evac must be turned off"); 104 } 105 106 void ShenandoahEvacOOMHandler::handle_out_of_memory_during_evacuation() { 107 assert(ShenandoahThreadLocalData::is_evac_allowed(Thread::current()), "sanity"); 108 assert(!ShenandoahThreadLocalData::is_oom_during_evac(Thread::current()), "TL oom-during-evac must not be set"); 109 110 jint threads_in_evac = Atomic::load_acquire(&_threads_in_evac); 111 while (true) { 112 jint other = Atomic::cmpxchg(&_threads_in_evac, threads_in_evac, (threads_in_evac - 1) | OOM_MARKER_MASK); 113 if (other == threads_in_evac) { 114 // Success: wait for other threads to get out of the protocol and return. 115 wait_for_no_evac_threads(); 116 return; 117 } else { 118 // Failure: try again with updated new value. 119 threads_in_evac = other; 120 } 121 } 122 } 123 124 void ShenandoahEvacOOMHandler::clear() { 125 assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "must be at a safepoint"); 126 assert((Atomic::load_acquire(&_threads_in_evac) & ~OOM_MARKER_MASK) == 0, "sanity"); 127 Atomic::release_store_fence(&_threads_in_evac, (jint)0); 128 } 129 130 ShenandoahEvacOOMScope::ShenandoahEvacOOMScope() { 131 ShenandoahHeap::heap()->enter_evacuation(); 132 } 133 134 ShenandoahEvacOOMScope::~ShenandoahEvacOOMScope() { 135 ShenandoahHeap::heap()->leave_evacuation(); 136 }