1 /* 2 * Copyright (c) 2018, Red Hat, Inc. and/or its affiliates. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. 7 * 8 * This code is distributed in the hope that it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 11 * version 2 for more details (a copy is included in the LICENSE file that 12 * accompanied this code). 13 * 14 * You should have received a copy of the GNU General Public License version 15 * 2 along with this work; if not, write to the Free Software Foundation, 16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17 * 18 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 19 * or visit www.oracle.com if you need additional information or have any 20 * questions. 21 * 22 */ 23 24 #include "precompiled.hpp" 25 26 #include "gc_implementation/shenandoah/shenandoahHeap.hpp" 27 #include "gc_implementation/shenandoah/shenandoahUtils.hpp" 28 #include "gc_implementation/shenandoah/shenandoahEvacOOMHandler.hpp" 29 30 const jint ShenandoahEvacOOMHandler::OOM_MARKER_MASK = 0x80000000; 31 32 ShenandoahEvacOOMHandler::ShenandoahEvacOOMHandler() : 33 _threads_in_evac(0) { 34 } 35 36 void ShenandoahEvacOOMHandler::wait_for_no_evac_threads() { 37 while ((OrderAccess::load_acquire(&_threads_in_evac) & ~OOM_MARKER_MASK) != 0) { 38 os::naked_short_sleep(1); 39 } 40 // At this point we are sure that no threads can evacuate anything. Raise 41 // the thread-local oom_during_evac flag to indicate that any attempt 42 // to evacuate should simply return the forwarding pointer instead (which is safe now). 43 Thread::current()->set_oom_during_evac(true); 44 } 45 46 void ShenandoahEvacOOMHandler::enter_evacuation() { 47 jint threads_in_evac = OrderAccess::load_acquire(&_threads_in_evac); 48 49 assert(!Thread::current()->is_evac_allowed(), "sanity"); 50 assert(!Thread::current()->is_oom_during_evac(), "TL oom-during-evac must not be set"); 51 52 if ((threads_in_evac & OOM_MARKER_MASK) != 0) { 53 wait_for_no_evac_threads(); 54 return; 55 } 56 57 while (true) { 58 jint other = Atomic::cmpxchg(threads_in_evac + 1, &_threads_in_evac, threads_in_evac); 59 if (other == threads_in_evac) { 60 // Success: caller may safely enter evacuation 61 DEBUG_ONLY(Thread::current()->set_evac_allowed(true)); 62 return; 63 } else { 64 // Failure: 65 // - if offender has OOM_MARKER_MASK, then loop until no more threads in evac 66 // - otherwise re-try CAS 67 if ((other & OOM_MARKER_MASK) != 0) { 68 wait_for_no_evac_threads(); 69 return; 70 } 71 threads_in_evac = other; 72 } 73 } 74 } 75 76 void ShenandoahEvacOOMHandler::leave_evacuation() { 77 if (!Thread::current()->is_oom_during_evac()) { 78 assert((OrderAccess::load_acquire(&_threads_in_evac) & ~OOM_MARKER_MASK) > 0, "sanity"); 79 // NOTE: It's ok to simply decrement, even with mask set, because unmasked value is positive. 80 Atomic::dec(&_threads_in_evac); 81 } else { 82 // If we get here, the current thread has already gone through the 83 // OOM-during-evac protocol and has thus either never entered or successfully left 84 // the evacuation region. Simply flip its TL oom-during-evac flag back off. 85 Thread::current()->set_oom_during_evac(false); 86 } 87 DEBUG_ONLY(Thread::current()->set_evac_allowed(false)); 88 assert(!Thread::current()->is_oom_during_evac(), "TL oom-during-evac must be turned off"); 89 } 90 91 void ShenandoahEvacOOMHandler::handle_out_of_memory_during_evacuation() { 92 assert(Thread::current()->is_evac_allowed(), "sanity"); 93 assert(!Thread::current()->is_oom_during_evac(), "TL oom-during-evac must not be set"); 94 95 jint threads_in_evac = OrderAccess::load_acquire(&_threads_in_evac); 96 while (true) { 97 jint other = Atomic::cmpxchg((threads_in_evac - 1) | OOM_MARKER_MASK, 98 &_threads_in_evac, threads_in_evac); 99 if (other == threads_in_evac) { 100 // Success: wait for other threads to get out of the protocol and return. 101 wait_for_no_evac_threads(); 102 return; 103 } else { 104 // Failure: try again with updated new value. 105 threads_in_evac = other; 106 } 107 } 108 } 109 110 void ShenandoahEvacOOMHandler::clear() { 111 assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "must be at a safepoint"); 112 assert((OrderAccess::load_acquire(&_threads_in_evac) & ~OOM_MARKER_MASK) == 0, "sanity"); 113 OrderAccess::release_store_fence(&_threads_in_evac, 0); 114 } 115 116 ShenandoahEvacOOMScope::ShenandoahEvacOOMScope() { 117 ShenandoahHeap::heap()->enter_evacuation(); 118 } 119 120 ShenandoahEvacOOMScope::~ShenandoahEvacOOMScope() { 121 ShenandoahHeap::heap()->leave_evacuation(); 122 } 123 124 ShenandoahEvacOOMScopeLeaver::ShenandoahEvacOOMScopeLeaver() { 125 ShenandoahHeap::heap()->leave_evacuation(); 126 } 127 128 ShenandoahEvacOOMScopeLeaver::~ShenandoahEvacOOMScopeLeaver() { 129 ShenandoahHeap::heap()->enter_evacuation(); 130 }