Index: rvm/src/org/jikesrvm/scheduler/VM_Processor.java =================================================================== --- rvm/src/org/jikesrvm/scheduler/VM_Processor.java (.../fixOSRTrigger) (revision 11861) +++ rvm/src/org/jikesrvm/scheduler/VM_Processor.java (.../simpleLockAlloc) (revision 11861) @@ -273,22 +273,6 @@ */ public int pthread_id; - /* - * book keeping for thick locks, a range of thick locks is held per processor - */ - /** last lock in VM_Lock available for this processor */ - public int lastLockIndex; - /** next lock in VM_Lock available for this processor */ - public int nextLockIndex; - /** a free lock */ - public VM_Lock freeLock; - /** number of free locks */ - public int freeLocks; - /** number of lock allocation operations on processor */ - public int locksAllocated; - /** number of lock free operations on processor */ - public int locksFreed; - // to handle contention for processor locks // VM_ProcessorLock awaitingProcessorLock; @@ -314,7 +298,6 @@ if (VM.BuildForIA32 && VM.runningVM) this.jtoc = VM_Magic.getJTOC(); this.id = id; - this.lastLockIndex = -1; this.vpStatus = IN_JAVA; } @@ -428,21 +411,6 @@ * Fail if thread switching is disabled on this processor */ public abstract void failIfThreadSwitchingDisabled(); - - public void dumpLocks() { - VM.sysWrite(" processor "); - VM.sysWriteInt(id); - VM.sysWrite(": "); - VM.sysWriteInt(locksAllocated); - VM.sysWrite(" locks allocated, "); - VM.sysWriteInt(locksFreed); - VM.sysWrite(" locks freed, "); - VM.sysWriteInt(freeLocks); - VM.sysWrite(" free looks, "); - int unallocated = lastLockIndex - nextLockIndex + 1; - VM.sysWriteInt(unallocated); - VM.sysWrite(" unallocated slots\n"); - } } /* For the emacs weenies in the crowd. Index: rvm/src/org/jikesrvm/scheduler/VM_Thread.java =================================================================== --- rvm/src/org/jikesrvm/scheduler/VM_Thread.java (.../fixOSRTrigger) (revision 11861) +++ rvm/src/org/jikesrvm/scheduler/VM_Thread.java (.../simpleLockAlloc) (revision 11861) @@ -267,6 +267,10 @@ /** Count of recursive uncaught exceptions, we need to bail out at some point */ private int uncaughtExceptionCount = 0; + + /** A cached free lock. Not a free list; this will only ever contain + * one lock! */ + public VM_Lock cachedFreeLock; /* * Wait/notify fields @@ -767,6 +771,13 @@ state = State.TERMINATED; notifyAll(this); } + + // return cached free lock + if (cachedFreeLock != null) { + VM_Lock.returnLock(cachedFreeLock); + cachedFreeLock = null; + } + // become another thread // VM_Scheduler.releaseThreadSlot(threadSlot, this); Index: rvm/src/org/jikesrvm/scheduler/VM_Lock.java =================================================================== --- rvm/src/org/jikesrvm/scheduler/VM_Lock.java (.../fixOSRTrigger) (revision 11861) +++ rvm/src/org/jikesrvm/scheduler/VM_Lock.java (.../simpleLockAlloc) (revision 11861) @@ -119,12 +119,6 @@ * Constants */ - /** - * Should we attempt to keep the roughly equal sized pools for free - * heavy-weight locks on each processor? - */ - protected static final boolean BALANCE_FREE_LOCKS = false; - /** Control the gathering of statistics */ public static final boolean STATS = false; @@ -136,8 +130,6 @@ protected static final int LOCK_CHUNK_SIZE = 1 << LOG_LOCK_CHUNK_SIZE; /** The mask used to get the chunk-level index */ protected static final int LOCK_CHUNK_MASK = LOCK_CHUNK_SIZE - 1; - /** The number of locks allocated at a time */ - protected static final int LOCK_ALLOCATION_UNIT_SIZE = 128; /** The maximum possible number of locks */ protected static final int MAX_LOCKS = LOCK_SPINE_SIZE * LOCK_CHUNK_SIZE; /** The number of chunks to allocate on startup */ @@ -151,8 +143,9 @@ private static final VM_ProcessorLock lockAllocationMutex = new VM_ProcessorLock(); /** The number of chunks in the spine that have been physically allocated */ private static int chunksAllocated; - /** The number of locks in the table that have been given out to processors */ - private static int lockUnitsAllocated; + /** The number of locks allocated (these may either be in use, on a global + * freelist, or on a thread's freelist. */ + private static int nextLockIndex; // Global free list. @@ -160,6 +153,10 @@ private static VM_Lock globalFreeLock; /** the number of locks held on the global free list. */ private static int globalFreeLocks; + /** the total number of allocation operations. */ + private static int globalLocksAllocated; + /** the total number of free operations. */ + private static int globalLocksFreed; // Statistics @@ -320,6 +317,7 @@ */ @Interruptible public static void init() { + nextLockIndex = 1; locks = new VM_Lock[LOCK_SPINE_SIZE][]; for (int i=0; i < INITIAL_CHUNKS; i++) { chunksAllocated++; @@ -349,41 +347,55 @@ /* Collector threads can't use heavy locks because they don't fix up their stacks after moving objects */ return null; } - if ((mine.freeLocks == 0) && (0 < globalFreeLocks) && BALANCE_FREE_LOCKS) { - localizeFreeLocks(mine); + VM_Thread me=VM_Scheduler.getCurrentThread(); + if (me.cachedFreeLock != null) { + VM_Lock l = me.cachedFreeLock; + me.cachedFreeLock = null; + return l; } - VM_Lock l = mine.freeLock; - if (l != null) { - mine.freeLock = l.nextFreeLock; - l.nextFreeLock = null; - mine.freeLocks--; - l.active = true; - } else { - l = new VM_Scheduler.LockModel(); // may cause thread switch (and processor loss) - mine = VM_Processor.getCurrentProcessor(); - if (mine.lastLockIndex < mine.nextLockIndex) { - lockAllocationMutex.lock("lock allocation mutex - allocating"); - mine.nextLockIndex = 1 + (LOCK_ALLOCATION_UNIT_SIZE * lockUnitsAllocated++); - lockAllocationMutex.unlock(); - mine.lastLockIndex = mine.nextLockIndex + LOCK_ALLOCATION_UNIT_SIZE - 1; - if (MAX_LOCKS <= mine.lastLockIndex) { - VM.sysWriteln("Too many fat locks on processor ", mine.id); // make MAX_LOCKS bigger? we can keep going?? - VM.sysFail("Exiting VM with fatal error"); - return null; - } + + VM_Lock l = null; + while (l == null) { + if (globalFreeLock != null) { + lockAllocationMutex.lock( + "lock allocation mutex - attempting freelist allocation"); + l = globalFreeLock; + if (l != null) { + globalFreeLock = l.nextFreeLock; + l.nextFreeLock = null; + l.active = true; + globalFreeLocks--; + } + lockAllocationMutex.unlock(); + } else { + l = new VM_Scheduler.LockModel(); // may cause thread switch (and processor loss) + lockAllocationMutex.lock( + "lock allocation mutex - attempting to add a lock"); + if (globalFreeLock == null) { + // ok, it's still correct for us to be adding a new lock + if (nextLockIndex >= MAX_LOCKS) { + VM.sysWriteln("Too many fat locks"); // make MAX_LOCKS bigger? we can keep going?? + VM.sysFail("Exiting VM with fatal error"); + } + l.index = nextLockIndex++; + globalLocksAllocated++; + } else { + l = null; // someone added to the freelist, try again + } + lockAllocationMutex.unlock(); + if (l != null) { + if (l.index >= numLocks()) { + /* We need to grow the table */ + growLocks(l.index); + } + addLock(l); + l.active = true; + /* make sure other processors see lock initialization. + * Note: Derek and I BELIEVE that an isync is not required in the other processor because the lock is newly allocated - Bowen */ + VM_Magic.sync(); + } } - l.index = mine.nextLockIndex++; - if (l.index >= numLocks()) { - /* We need to grow the table */ - growLocks(l.index); - } - addLock(l); - l.active = true; - /* make sure other processors see lock initialization. - * Note: Derek and I BELIEVE that an isync is not required in the other processor because the lock is newly allocated - Bowen */ - VM_Magic.sync(); } - mine.locksAllocated++; return l; } @@ -394,12 +406,23 @@ */ protected static void free(VM_Lock l) { l.active = false; - VM_Processor mine = VM_Processor.getCurrentProcessor(); - l.nextFreeLock = mine.freeLock; - mine.freeLock = l; - mine.freeLocks++; - mine.locksFreed++; + VM_Thread me = VM_Scheduler.getCurrentThread(); + if (me.cachedFreeLock == null) { + me.cachedFreeLock = l; + } else { + returnLock(l); + } } + + static void returnLock(VM_Lock l) { + lockAllocationMutex.lock( + "lock allocation mutex - returning lock to freelist"); + l.nextFreeLock = globalFreeLock; + globalFreeLock = l; + globalFreeLocks++; + globalLocksFreed++; + lockAllocationMutex.unlock(); + } /** * Grow the locks table by allocating a new spine chunk. @@ -430,76 +453,6 @@ } /** - * Transfers free heavy-weight locks from a processor local - * structure to a global one. - * - * Only used if RVM_WITH_FREE_LOCK_BALANCING preprocessor - * directive is set for the current build. - */ - protected static void globalizeFreeLocks(VM_Processor mine) { - if (mine.freeLocks <= LOCK_ALLOCATION_UNIT_SIZE) { - if (VM.VerifyAssertions) VM._assert(mine.freeLock != null); - VM_Lock q = mine.freeLock; - while (q.nextFreeLock != null) { - q = q.nextFreeLock; - } - lockAllocationMutex.lock("lock allocation mutex for globalizing locks"); - q.nextFreeLock = globalFreeLock; - globalFreeLock = mine.freeLock; - globalFreeLocks += mine.freeLocks; - lockAllocationMutex.unlock(); - mine.freeLock = null; - mine.freeLocks = 0; - } else { - VM_Lock p = null; - VM_Lock q = mine.freeLock; - for (int i = 0; i < LOCK_ALLOCATION_UNIT_SIZE; i++) { - p = q; - q = q.nextFreeLock; - } - lockAllocationMutex.lock("lock allocation mutex for globalizing locks"); - p.nextFreeLock = globalFreeLock; - globalFreeLock = mine.freeLock; - globalFreeLocks += LOCK_ALLOCATION_UNIT_SIZE; - lockAllocationMutex.unlock(); - mine.freeLock = q; - mine.freeLocks -= LOCK_ALLOCATION_UNIT_SIZE; - } - } - - /** - * Transfers free heavy-weight locks from a global structure to a - * processor local one. - * - * Only used if RVM_WITH_FREE_LOCK_BALANCING preprocessor - * directive is set for the current build. - */ - private static void localizeFreeLocks(VM_Processor mine) { - if (true) return; // TEMP - if (VM.VerifyAssertions) VM._assert(mine.freeLock == null); - lockAllocationMutex.lock("lock allocation mutex for localize"); - if (globalFreeLocks <= LOCK_ALLOCATION_UNIT_SIZE) { - mine.freeLock = globalFreeLock; - mine.freeLocks = globalFreeLocks; - globalFreeLock = null; - globalFreeLocks = 0; - } else { - VM_Lock p = null; - VM_Lock q = globalFreeLock; - for (int i = 0; i < LOCK_ALLOCATION_UNIT_SIZE; i++) { - p = q; - q = q.nextFreeLock; - } - p.nextFreeLock = null; - mine.freeLock = globalFreeLock; - mine.freeLocks = LOCK_ALLOCATION_UNIT_SIZE; - globalFreeLock = q; - globalFreeLocks -= LOCK_ALLOCATION_UNIT_SIZE; - } - lockAllocationMutex.unlock(); - } - - /** * Return the number of lock slots that have been allocated. This provides * the range of valid lock ids. */ @@ -539,6 +492,14 @@ } } VM.sysWrite("\n"); + + VM.sysWrite("lock availability stats: "); + VM.sysWriteInt(globalLocksAllocated); + VM.sysWrite(" locks allocated, "); + VM.sysWriteInt(globalLocksFreed); + VM.sysWrite(" locks freed, "); + VM.sysWriteInt(globalFreeLocks); + VM.sysWrite(" free locks\n"); } /** Index: rvm/src/org/jikesrvm/scheduler/greenthreads/VM_GreenScheduler.java =================================================================== --- rvm/src/org/jikesrvm/scheduler/greenthreads/VM_GreenScheduler.java (.../fixOSRTrigger) (revision 11861) +++ rvm/src/org/jikesrvm/scheduler/greenthreads/VM_GreenScheduler.java (.../simpleLockAlloc) (revision 11861) @@ -497,13 +497,6 @@ } VM.sysWrite("\n"); - VM.sysWrite("\n-- Locks available --\n"); - for (int i = PRIMORDIAL_PROCESSOR_ID; i <= numProcessors; ++i) { - processor = getProcessor(i); - processor.dumpLocks(); - } - VM.sysWrite("\n"); - VM.sysWrite("\n-- Locks in use --\n"); VM_Lock.dumpLocks(); Index: rvm/src/org/jikesrvm/scheduler/greenthreads/VM_GreenLock.java =================================================================== --- rvm/src/org/jikesrvm/scheduler/greenthreads/VM_GreenLock.java (.../fixOSRTrigger) (revision 11861) +++ rvm/src/org/jikesrvm/scheduler/greenthreads/VM_GreenLock.java (.../simpleLockAlloc) (revision 11861) @@ -124,9 +124,6 @@ } } mutex.unlock(); // does a VM_Magic.sync(); (thread-switching benign) - if (deflated && ((LOCK_ALLOCATION_UNIT_SIZE << 1) <= mine.freeLocks) && BALANCE_FREE_LOCKS) { - globalizeFreeLocks(mine); - } } /**