objsync.c
来自「This is a resource based on j2me embedde」· C语言 代码 · 共 1,810 行 · 第 1/5 页
C
1,810 行
/* * @(#)objsync.c 1.100 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program 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 program 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 at /legal/license.txt). * * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */#include "javavm/include/defs.h"#include "javavm/include/sync.h"#include "javavm/include/indirectmem.h"#include "javavm/include/localroots.h"#include "javavm/include/common_exceptions.h"#include "javavm/include/clib.h"#ifdef CVM_JVMTI#include "javavm/include/jvmtiExport.h"#endif/* NOTES: About CVM object monitors and their life-cycles ====================================================== The Fast Locking mechanism: ========================== CVM implements a fast locking mechanism which attempts to speed up monitor locking/unlocking operations for the monitors which meet the following criteria: 1. The object monitor is locked and unlocked only by one thread at a time (i.e. no thread contention occurs while trying to lock or unlock the monitor). 2. The object monitor isn't used for the Object.wait(), Object.notify(), Object.notifyAll(). If the above criteria is met, then the lock is implemented by simply setting the low 2 bits of a header bit flag word in each object using some form of atomic operation. The fast lock mechanism basically provides some speed optimization benefits due to: 1. Not having to actually invoke OS system calls to allocate and lock/unlock a monitor. 2. Reduce the amount of memory/resource allocations necessary to implement the monitor. Monitors are allocated per thread as well as globally and are reclaimed for reuse through various scavenging mechanism. This reuse speeds up monitor operations if a previously reclaimed monitor is available for reuse. At any time that the criteria for fast locking/unlocking are not met, the monitor implementation will inflate itself into a heavy weight monitor which relies on the platform-specific mutex and condvar implementations defined by the porting layer to do its locking/unlocking. Data Structures & Ownership of the data structures: ================================================== CVM object monitors are implemented using 2 data structures: 1. The CVMOwnedMonitor record. 2. The CVMObjMonitor monitor implementation. The CVMOwnedMonitor record is used to track locking information with or without an actual monitor. CVMOwnedMonitors are allocated, freed, and modified exclusively by its owner thread. This avoids contention between threads over the manipulation of CVMOwnedMonitors. The CVMObjMonitor is the actual monitor that is attached to the object when a heavy-weight lock needs to employed. CVMObjMonitors, unlike CVMOwnedMonitors, are shared between threads and held in the global list. Contention is minimized by having each thread cache one instance of the CVMObjMonitor for its own use during the inflation process. Otherwise, CVMObjMonitors in the global lists are protected from contention between threads by the CVMglobals.syncLock. In CVMglobals, there are: 1. CVMglobals.objLocksBound: This is the list of bound monitors i.e. CVMObjMonitors which are attached to an object. 2. CVMglobals.objLocksUnbound: This is the list of unbound monitors i.e. CVMObjMonitors which used to be attached to objects which have now been GC'ed. This means that the monitor's object pointer is NULL. 3. CVMglobals.objLocksFree: This is the list of free monitors i.e CVMObjMonitors which are not attached to any objects yet. 4. CVMglobals.syncLock: This is the global lock used for protecting the above 3 monitor lists and for synchronizing threads which wish to: inflate a monitor, set an object hash code, or do an unlock operation on a fast lock. Each thread's CVMExecEnv has: 1. ee->objLockCurrent: This is a per-thread reference to a CVMObjMonitor that is in the midst of being processed and has not yet been attached to an object yet. This is necessary to prevent the referenced CVMObjMonitor from being scavenged prematurely. 2. ee->objLocksFreeUnlocked: This is supply of CVMObjMonitors for use by the the thread in the event that it needs to do a monitor inflation. The inflation process will replenish ee->objLocksFreeUnlocked at the end of the inflation process. The current implementation only keeps one CVMObjMonitor available in ee->objLocksFreeUnlocked. 3. ee->objLocksOwned: This is a list of all the CVMOwnedMonitors which are locked by the thread. These CVMOwnedMonitors may or may not be attached to an actual CVMObjMonitor. 4. ee->objLocksFreeOwned: This is a list of free CVMOwnedMonitors available for the thread's use. Write access to these fields of the CVMExecEnv is exclusively owned by owner thread. This avoids any contention between threads, and, hence, no synchronization mechanism is needed to synchronize accesses to these fields. The Monitor Life-Cycles & Processes: =================================== Inflation: . This is the process by which an object gets attached to a CVMObjMonitor. This process is protected by the CVMglobals.syncLock. Initial state: . The object header bits indicate CVM_LOCKSTATE_UNLOCKED. Locked without contention (fast lock): . The object header bits indicate CVM_LOCKSTATE_LOCKED and holds a pointer to a CVMOwnedMonitor instance. . The thread who owns the lock on that object also holds a pointer to the same CVMOwnedMonitor in its owned list. . If the object header bits already indicate CVM_LOCKSTATE_LOCKED upon attempting to lock the monitor, then the CVMOwnedMonitor's reentry count is incremented. Locked with contention (heavy weight lock): . The object header bits indicate CVM_LOCKSTATE_MONITOR and holds a pointer to a CVMObjMonitor instance. . The CVMObjMonitor is marked as CVM_OBJMON_OWNED and put on the global bound list. . The thread who owns the lock still have a pointer to the CVMOwnedMonitor record. The CVMOwnedMonitor record has a pointer to the CVMObjMonitor for the lock. Unlocked while holding a fast lock: . The CVMOwnedMonitor's reentry count is decremented. If this count has not reached 0 yet, then the unlock operation is done. Otherwise, the following steps are executed as well. . The object header bits are changed back to CVM_LOCKSTATE_UNLOCKED. . The CVMOwnedMonitor is moved from the owner thread's ee's owned list to its free list. Unlocked while holding a heavy weight lock: . The object header bits remains CVM_LOCKSTATE_MONITOR and the object retains its pointer to the CVMObjMonitor instance. . The CVMObjMonitor is marked as CVM_OBJMON_BOUND and remains on the global bound list. Each thread always keep one free CVMObjMonitor around to accelerate impending monitor inflation operations. After a monitor has been inflated, the thread will attempt to replenish its spare free CVMObjMonitor. It does this by either: 1. Getting one from the global free list. 2. Scavenging for bound monitors which are not locked. 3. Allocating new ones. During CVMObjMonitor scavenging: . CVMObjMonitors which are bound (on the global bound list) but not locked are released into the global free list. . CVMObjMonitors which are on the global unbound list (see notes on monitor GC below) are unlocked and released into the global free list. CVMOwnedMonitor scavenging: . CVMOwnedMonitor needed by most monitor operations and attained before the operation commences (at the top of the functions). . Usually, it is attained from the thread's free list. If the free list is empty, then the thread will attempt to replenish the free CVMOwnedMonitor supply by one of the following ways: 1. Allocate a new one. 2. Run the scavenger which searches the thread's owned list for monitors which have fast locks but are no longer associated with a live object (see notes on monitor GC below). These monitor records are unlocked and released back into the thread's free list. Since this case does not naturally happen with Java code, it is unlikely to occur. Hence a scavenging operation is unlikely to yield a free CVMOwnedMonitor. This is why we opt to allocate a new one first before attempting a scavenging operation. During a monitor GC (invoked by the GC): . CVMObjMonitors which are locked but no longer associated with a live object (because the object has been GC'ed but the lock hasn't been released prior) are moved from the global bound list to the global unbound list. See notes on monitor scavenging above to see how these monitors get released back into the free lists for reuse. The CVMObjMonitors do not get moved directly from the bound list to the free list because bound CVMObjMonitors are attached to CVMOwnedMonitors. Hence, the best that the GC thread can do is to tag the monitor by putting it on the unbound list and relying on the owner thread to release the attached CVMOwnedMonitor as well as put the CVMObjMonitor back on the free list. During thread destruction: . When a thread shuts down, it will unlock all monitors whose lock it owns if those monitors are still associated with live objects. . While attempting to unlock monitors, if the thread encounters any monitors which are no longer associated with any live objects, it will invoke the appropriate scavengers to reclaim the monitors into the free lists. . After reclaiming or unlocking all the monitors on its owned list, the thread will release/free up all the free monitors on its free list before exiting. Deflation of heavy weight monitors: ================================== Heavy weight monitors are deflated in the scavenging process. Since a scavenging process is invoked immediately after each monitor inflation process, unlocked inflated monitors don't stay around for long. Synchronization between various Monitor Methods: =============================================== The following methods must be protected from themselves and each other by sync'ing on the same lock. The lock currently used for this purpose is CVMglobals.syncLock. The methods are as follows: 1. CVMobjectInflate() 2. CVMobjectSetHashBitsIfNeeded() 3. CVMfastUnlock() Assumptions: =========== 1. It is assumed that we won't ever enter a monitor recursively up to an reentry count of CVM_MAX_REENTRY_COUNT. This allows CVM_INVALID_REENTRY_COUNT to be above CVM_MAX_REENTRY_COUNT. Debug builds will check that count never reaches CVM_MAX_REENTRY_COUNT.*/#ifndef CVM_INITIAL_NUMBER_OF_OBJ_MONITORS#define CVM_INITIAL_NUMBER_OF_OBJ_MONITORS 10#endifstatic void CVMmonitorAttachObjMonitor2OwnedMonitor(CVMExecEnv *ee, CVMObjMonitor *mon);#if CVM_FASTLOCK_TYPE != CVM_FASTLOCK_NONEstatic CVMBool CVMfastReentryTryLock(CVMExecEnv *ee, CVMObject *obj);#endifstatic void CVMsyncMonitorScavenge(CVMExecEnv *ee, CVMBool releaseOwnedRecs);static void CVMsyncGCSafeAllMonitorScavengeInternal(CVMExecEnv *ee, CVMBool releaseOwnedRecs);static void CVMmonitorScavengeFast(CVMExecEnv *ee);static void CVMownedMonitorInit(CVMOwnedMonitor *m, CVMExecEnv *owner);static void CVMownedMonitorDestroy(CVMOwnedMonitor *m);#ifdef CVM_THREAD_BOOSTstatic void CVMboost(CVMExecEnv *ee, CVMObjMonitor *mon, CVMExecEnv *owner);static void CVMunboost(CVMExecEnv *ee, CVMObjMonitor *mon);#endifstatic CVMTryLockFunc CVMdetTryUnlock, CVMdetTryLock;static CVMLockFunc CVMfastLock, CVMdetLock;static CVMLockFunc CVMfastUnlock, CVMdetUnlock;static CVMNotifyFunc CVMfastNotify, CVMdetNotify;static CVMNotifyAllFunc CVMfastNotifyAll, CVMdetNotifyAll;static CVMWaitFunc CVMfastWait, CVMdetWait;/* * The locking vector. */const CVMSyncVector CVMsyncKinds[2] = { {CVMfastTryLock, CVMfastLock, CVMfastTryUnlock, CVMfastUnlock, CVMfastWait, CVMfastNotify, CVMfastNotifyAll, NULL}, {CVMdetTryLock, CVMdetLock, CVMdetTryUnlock, CVMdetUnlock, CVMdetWait, CVMdetNotify, CVMdetNotifyAll, NULL}};#if CVM_FASTLOCK_TYPE == CVM_FASTLOCK_MICROLOCK/* NOTE: The rendezvousOwnedRec below is used as a fake CVMOwnedMonitor that is not owned by any thread (hence, the owner is NULL). It is used to replace the various words field in the object header when we want to inflate the monitor into a CVMObjMonitor. This replacement ensures that no fast locking and unlocking activity occurs on this object without having to hold onto the object microlock the whole time we're doing the inflation. This also avoids any complications due to any side effects of holding onto the object microlock while waiting for various HPI functions (e.g. CVMmutexSetOwner()) to complete.*/static const CVMOwnedMonitor rendezvousOwnedRec = { /* owner */ NULL, /* type */ CVM_OWNEDMON_FAST, /* object */ NULL, /* u */ {{0}}, /* next */ NULL,#ifdef CVM_DEBUG /* magic */ CVM_OWNEDMON_MAGIC, /* state */ CVM_OWNEDMON_OWNED,#endif /* count */ 1};#endifstatic voidCVMmonEnter(CVMExecEnv *ee, CVMObjMonitor *mon){ /* This is like a reference count, needed if count==0 */ ee->objLockCurrent = mon;#ifdef CVM_THREAD_BOOST if (mon->boost) { /* If we get here, then the ownership of the monitor has not actually been claimed the owner thread yet though it has been marked as so. The following ensures that ownership is claimed by the owner thread before we enter the monitor. */ CVMExecEnv *owner = CVMprofiledMonitorGetOwner(&mon->mon); if (owner == ee) { /* mon->boost being true and this thread being the owner implies that another thread had inflated this monitor for us. We'll need to claim ownership of the monitor and unboost our thread priority. Only after that we'll we re-enter the the monitor. */ CVMunboost(ee, mon); CVMassert(!mon->boost); CVMprofiledMonitorEnterUnsafe(&mon->mon, ee, CVM_TRUE); } else { /* This is the case where we discovered a contended enter and inflated the monitor. We therefore need to boost the thread priority of the owner and wait for it to claim ownership of the monitor before we try to acquire it ourselves. */ CVMprofiledMonitorPreContendedEnterUnsafe(&mon->mon, ee); CVMboost(ee, mon, owner); CVMprofiledMonitorContendedEnterUnsafe(&mon->mon, ee); } } else { CVMprofiledMonitorEnterUnsafe(&mon->mon, ee, CVM_TRUE); } CVMassert(!mon->boost);#else CVMprofiledMonitorEnterUnsafe(&mon->mon, ee, CVM_TRUE);#endif ee->objLockCurrent = NULL; CVMassert(CVMprofiledMonitorGetOwner(&mon->mon) == ee);}static CVMBoolCVMmonTryEnter(CVMExecEnv *ee, CVMObjMonitor *mon){#ifdef CVM_THREAD_BOOST if (mon->boost) { return CVM_FALSE; }#endif if (!CVMprofiledMonitorTryEnter(&mon->mon, ee)) { return CVM_FALSE;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?