Mercurial > hg > truffle
diff src/share/vm/prims/jvmtiRawMonitor.cpp @ 1878:fa83ab460c54
6988353: refactor contended sync subsystem
Summary: reduce complexity by factoring synchronizer.cpp
Reviewed-by: dholmes, never, coleenp
author | acorn |
---|---|
date | Fri, 22 Oct 2010 15:59:34 -0400 |
parents | |
children | f95d63e2154a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/prims/jvmtiRawMonitor.cpp Fri Oct 22 15:59:34 2010 -0400 @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2003, 2007, 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 "incls/_precompiled.incl" +# include "incls/_jvmtiRawMonitor.cpp.incl" + +GrowableArray<JvmtiRawMonitor*> *JvmtiPendingMonitors::_monitors = new (ResourceObj::C_HEAP) GrowableArray<JvmtiRawMonitor*>(1,true); + +void JvmtiPendingMonitors::transition_raw_monitors() { + assert((Threads::number_of_threads()==1), + "Java thread has not created yet or more than one java thread \ +is running. Raw monitor transition will not work"); + JavaThread *current_java_thread = JavaThread::current(); + assert(current_java_thread->thread_state() == _thread_in_vm, "Must be in vm"); + { + ThreadBlockInVM __tbivm(current_java_thread); + for(int i=0; i< count(); i++) { + JvmtiRawMonitor *rmonitor = monitors()->at(i); + int r = rmonitor->raw_enter(current_java_thread); + assert(r == ObjectMonitor::OM_OK, "raw_enter should have worked"); + } + } + // pending monitors are converted to real monitor so delete them all. + dispose(); +} + +// +// class JvmtiRawMonitor +// + +JvmtiRawMonitor::JvmtiRawMonitor(const char *name) { +#ifdef ASSERT + _name = strcpy(NEW_C_HEAP_ARRAY(char, strlen(name) + 1), name); +#else + _name = NULL; +#endif + _magic = JVMTI_RM_MAGIC; +} + +JvmtiRawMonitor::~JvmtiRawMonitor() { +#ifdef ASSERT + FreeHeap(_name); +#endif + _magic = 0; +} + + +bool +JvmtiRawMonitor::is_valid() { + int value = 0; + + // This object might not be a JvmtiRawMonitor so we can't assume + // the _magic field is properly aligned. Get the value in a safe + // way and then check against JVMTI_RM_MAGIC. + + switch (sizeof(_magic)) { + case 2: + value = Bytes::get_native_u2((address)&_magic); + break; + + case 4: + value = Bytes::get_native_u4((address)&_magic); + break; + + case 8: + value = Bytes::get_native_u8((address)&_magic); + break; + + default: + guarantee(false, "_magic field is an unexpected size"); + } + + return value == JVMTI_RM_MAGIC; +} + +// ------------------------------------------------------------------------- +// The raw monitor subsystem is entirely distinct from normal +// java-synchronization or jni-synchronization. raw monitors are not +// associated with objects. They can be implemented in any manner +// that makes sense. The original implementors decided to piggy-back +// the raw-monitor implementation on the existing Java objectMonitor mechanism. +// This flaw needs to fixed. We should reimplement raw monitors as sui-generis. +// Specifically, we should not implement raw monitors via java monitors. +// Time permitting, we should disentangle and deconvolve the two implementations +// and move the resulting raw monitor implementation over to the JVMTI directories. +// Ideally, the raw monitor implementation would be built on top of +// park-unpark and nothing else. +// +// raw monitors are used mainly by JVMTI +// The raw monitor implementation borrows the ObjectMonitor structure, +// but the operators are degenerate and extremely simple. +// +// Mixed use of a single objectMonitor instance -- as both a raw monitor +// and a normal java monitor -- is not permissible. +// +// Note that we use the single RawMonitor_lock to protect queue operations for +// _all_ raw monitors. This is a scalability impediment, but since raw monitor usage +// is deprecated and rare, this is not of concern. The RawMonitor_lock can not +// be held indefinitely. The critical sections must be short and bounded. +// +// ------------------------------------------------------------------------- + +int JvmtiRawMonitor::SimpleEnter (Thread * Self) { + for (;;) { + if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) { + return OS_OK ; + } + + ObjectWaiter Node (Self) ; + Self->_ParkEvent->reset() ; // strictly optional + Node.TState = ObjectWaiter::TS_ENTER ; + + RawMonitor_lock->lock_without_safepoint_check() ; + Node._next = _EntryList ; + _EntryList = &Node ; + OrderAccess::fence() ; + if (_owner == NULL && Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) { + _EntryList = Node._next ; + RawMonitor_lock->unlock() ; + return OS_OK ; + } + RawMonitor_lock->unlock() ; + while (Node.TState == ObjectWaiter::TS_ENTER) { + Self->_ParkEvent->park() ; + } + } +} + +int JvmtiRawMonitor::SimpleExit (Thread * Self) { + guarantee (_owner == Self, "invariant") ; + OrderAccess::release_store_ptr (&_owner, NULL) ; + OrderAccess::fence() ; + if (_EntryList == NULL) return OS_OK ; + ObjectWaiter * w ; + + RawMonitor_lock->lock_without_safepoint_check() ; + w = _EntryList ; + if (w != NULL) { + _EntryList = w->_next ; + } + RawMonitor_lock->unlock() ; + if (w != NULL) { + guarantee (w ->TState == ObjectWaiter::TS_ENTER, "invariant") ; + ParkEvent * ev = w->_event ; + w->TState = ObjectWaiter::TS_RUN ; + OrderAccess::fence() ; + ev->unpark() ; + } + return OS_OK ; +} + +int JvmtiRawMonitor::SimpleWait (Thread * Self, jlong millis) { + guarantee (_owner == Self , "invariant") ; + guarantee (_recursions == 0, "invariant") ; + + ObjectWaiter Node (Self) ; + Node._notified = 0 ; + Node.TState = ObjectWaiter::TS_WAIT ; + + RawMonitor_lock->lock_without_safepoint_check() ; + Node._next = _WaitSet ; + _WaitSet = &Node ; + RawMonitor_lock->unlock() ; + + SimpleExit (Self) ; + guarantee (_owner != Self, "invariant") ; + + int ret = OS_OK ; + if (millis <= 0) { + Self->_ParkEvent->park(); + } else { + ret = Self->_ParkEvent->park(millis); + } + + // If thread still resides on the waitset then unlink it. + // Double-checked locking -- the usage is safe in this context + // as we TState is volatile and the lock-unlock operators are + // serializing (barrier-equivalent). + + if (Node.TState == ObjectWaiter::TS_WAIT) { + RawMonitor_lock->lock_without_safepoint_check() ; + if (Node.TState == ObjectWaiter::TS_WAIT) { + // Simple O(n) unlink, but performance isn't critical here. + ObjectWaiter * p ; + ObjectWaiter * q = NULL ; + for (p = _WaitSet ; p != &Node; p = p->_next) { + q = p ; + } + guarantee (p == &Node, "invariant") ; + if (q == NULL) { + guarantee (p == _WaitSet, "invariant") ; + _WaitSet = p->_next ; + } else { + guarantee (p == q->_next, "invariant") ; + q->_next = p->_next ; + } + Node.TState = ObjectWaiter::TS_RUN ; + } + RawMonitor_lock->unlock() ; + } + + guarantee (Node.TState == ObjectWaiter::TS_RUN, "invariant") ; + SimpleEnter (Self) ; + + guarantee (_owner == Self, "invariant") ; + guarantee (_recursions == 0, "invariant") ; + return ret ; +} + +int JvmtiRawMonitor::SimpleNotify (Thread * Self, bool All) { + guarantee (_owner == Self, "invariant") ; + if (_WaitSet == NULL) return OS_OK ; + + // We have two options: + // A. Transfer the threads from the WaitSet to the EntryList + // B. Remove the thread from the WaitSet and unpark() it. + // + // We use (B), which is crude and results in lots of futile + // context switching. In particular (B) induces lots of contention. + + ParkEvent * ev = NULL ; // consider using a small auto array ... + RawMonitor_lock->lock_without_safepoint_check() ; + for (;;) { + ObjectWaiter * w = _WaitSet ; + if (w == NULL) break ; + _WaitSet = w->_next ; + if (ev != NULL) { ev->unpark(); ev = NULL; } + ev = w->_event ; + OrderAccess::loadstore() ; + w->TState = ObjectWaiter::TS_RUN ; + OrderAccess::storeload(); + if (!All) break ; + } + RawMonitor_lock->unlock() ; + if (ev != NULL) ev->unpark(); + return OS_OK ; +} + +// Any JavaThread will enter here with state _thread_blocked +int JvmtiRawMonitor::raw_enter(TRAPS) { + TEVENT (raw_enter) ; + void * Contended ; + + // don't enter raw monitor if thread is being externally suspended, it will + // surprise the suspender if a "suspended" thread can still enter monitor + JavaThread * jt = (JavaThread *)THREAD; + if (THREAD->is_Java_thread()) { + jt->SR_lock()->lock_without_safepoint_check(); + while (jt->is_external_suspend()) { + jt->SR_lock()->unlock(); + jt->java_suspend_self(); + jt->SR_lock()->lock_without_safepoint_check(); + } + // guarded by SR_lock to avoid racing with new external suspend requests. + Contended = Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) ; + jt->SR_lock()->unlock(); + } else { + Contended = Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) ; + } + + if (Contended == THREAD) { + _recursions ++ ; + return OM_OK ; + } + + if (Contended == NULL) { + guarantee (_owner == THREAD, "invariant") ; + guarantee (_recursions == 0, "invariant") ; + return OM_OK ; + } + + THREAD->set_current_pending_monitor(this); + + if (!THREAD->is_Java_thread()) { + // No other non-Java threads besides VM thread would acquire + // a raw monitor. + assert(THREAD->is_VM_thread(), "must be VM thread"); + SimpleEnter (THREAD) ; + } else { + guarantee (jt->thread_state() == _thread_blocked, "invariant") ; + for (;;) { + jt->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() + SimpleEnter (THREAD) ; + + // were we externally suspended while we were waiting? + if (!jt->handle_special_suspend_equivalent_condition()) break ; + + // This thread was externally suspended + // + // This logic isn't needed for JVMTI raw monitors, + // but doesn't hurt just in case the suspend rules change. This + // logic is needed for the JvmtiRawMonitor.wait() reentry phase. + // We have reentered the contended monitor, but while we were + // waiting another thread suspended us. We don't want to reenter + // the monitor while suspended because that would surprise the + // thread that suspended us. + // + // Drop the lock - + SimpleExit (THREAD) ; + + jt->java_suspend_self(); + } + + assert(_owner == THREAD, "Fatal error with monitor owner!"); + assert(_recursions == 0, "Fatal error with monitor recursions!"); + } + + THREAD->set_current_pending_monitor(NULL); + guarantee (_recursions == 0, "invariant") ; + return OM_OK; +} + +// Used mainly for JVMTI raw monitor implementation +// Also used for JvmtiRawMonitor::wait(). +int JvmtiRawMonitor::raw_exit(TRAPS) { + TEVENT (raw_exit) ; + if (THREAD != _owner) { + return OM_ILLEGAL_MONITOR_STATE; + } + if (_recursions > 0) { + --_recursions ; + return OM_OK ; + } + + void * List = _EntryList ; + SimpleExit (THREAD) ; + + return OM_OK; +} + +// Used for JVMTI raw monitor implementation. +// All JavaThreads will enter here with state _thread_blocked + +int JvmtiRawMonitor::raw_wait(jlong millis, bool interruptible, TRAPS) { + TEVENT (raw_wait) ; + if (THREAD != _owner) { + return OM_ILLEGAL_MONITOR_STATE; + } + + // To avoid spurious wakeups we reset the parkevent -- This is strictly optional. + // The caller must be able to tolerate spurious returns from raw_wait(). + THREAD->_ParkEvent->reset() ; + OrderAccess::fence() ; + + // check interrupt event + if (interruptible && Thread::is_interrupted(THREAD, true)) { + return OM_INTERRUPTED; + } + + intptr_t save = _recursions ; + _recursions = 0 ; + _waiters ++ ; + if (THREAD->is_Java_thread()) { + guarantee (((JavaThread *) THREAD)->thread_state() == _thread_blocked, "invariant") ; + ((JavaThread *)THREAD)->set_suspend_equivalent(); + } + int rv = SimpleWait (THREAD, millis) ; + _recursions = save ; + _waiters -- ; + + guarantee (THREAD == _owner, "invariant") ; + if (THREAD->is_Java_thread()) { + JavaThread * jSelf = (JavaThread *) THREAD ; + for (;;) { + if (!jSelf->handle_special_suspend_equivalent_condition()) break ; + SimpleExit (THREAD) ; + jSelf->java_suspend_self(); + SimpleEnter (THREAD) ; + jSelf->set_suspend_equivalent() ; + } + } + guarantee (THREAD == _owner, "invariant") ; + + if (interruptible && Thread::is_interrupted(THREAD, true)) { + return OM_INTERRUPTED; + } + return OM_OK ; +} + +int JvmtiRawMonitor::raw_notify(TRAPS) { + TEVENT (raw_notify) ; + if (THREAD != _owner) { + return OM_ILLEGAL_MONITOR_STATE; + } + SimpleNotify (THREAD, false) ; + return OM_OK; +} + +int JvmtiRawMonitor::raw_notifyAll(TRAPS) { + TEVENT (raw_notifyAll) ; + if (THREAD != _owner) { + return OM_ILLEGAL_MONITOR_STATE; + } + SimpleNotify (THREAD, true) ; + return OM_OK; +} +