001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.jikesrvm.mm.mmtk; 014 015 import org.jikesrvm.VM; 016 017 import org.vmmagic.unboxed.*; 018 import org.vmmagic.pragma.*; 019 020 import org.jikesrvm.runtime.Entrypoints; 021 import org.jikesrvm.runtime.Magic; 022 import org.jikesrvm.scheduler.RVMThread; 023 import org.jikesrvm.scheduler.ThreadQueue; 024 025 /** 026 * Adaptive mutex with a spinlock fast path. Designed for good performance 027 * on native threaded systems. This implementation has the following specific 028 * properties: 029 * <ul> 030 * <li>It behaves like a spinlock on the fast path (one CAS to lock, one CAS 031 * to unlock, if there is no contention).</li> 032 * <li>It has the ability to spin for some predetermined number of cycles 033 * (see <code>SPIN_LIMIT</code>).</li> 034 * <li>Adapts to contention by eventually placing contending threads on a 035 * queue and parking them.</li> 036 * <li>Has a weak fairness guarantee: contenders follow queue discipline, 037 * except that new contenders may "beat" the thread at the head of the 038 * queue if they arrive just as the lock becomes available and the thread 039 * at the head of the queue just got scheduled.</li> 040 * </ul> 041 * @author Filip Pizlo 042 */ 043 @Uninterruptible public class Lock extends org.mmtk.vm.Lock { 044 045 // Core Instance fields 046 private String name; // logical name of lock 047 private final int id; // lock id (based on a non-resetting counter) 048 private static int lockCount; 049 private static final int SPIN_LIMIT = 1000; 050 /** Lock is not held and the queue is empty. When the lock is in this 051 * state, there <i>may</i> be a thread that just got dequeued and is 052 * about to enter into contention on the lock. */ 053 private static final int CLEAR = 0; 054 /** Lock is held and the queue is empty. */ 055 private static final int LOCKED = 1; 056 /** Lock is not held but the queue is non-empty. This state guarantees 057 * that there is a thread that got dequeued and is about to contend on 058 * the lock. */ 059 private static final int CLEAR_QUEUED = 2; 060 /** Lock is held and the queue is non-empty. */ 061 private static final int LOCKED_QUEUED = 3; 062 /** Some thread is currently engaged in an enqueue or dequeue operation, 063 * and will return the lock to whatever it was in previously once that 064 * operation is done. During this states any lock/unlock attempts will 065 * spin until the lock reverts to some other state. */ 066 private static final int QUEUEING = 4; 067 private ThreadQueue queue; 068 @Entrypoint 069 private int state; 070 071 // Diagnosis Instance fields 072 @Untraced 073 private RVMThread thread; // if locked, who locked it? 074 private int where = -1; // how far along has the lock owner progressed? 075 public Lock(String name) { 076 this(); 077 this.name = name; 078 } 079 080 public Lock() { 081 id = lockCount++; 082 queue = new ThreadQueue(); 083 state = CLEAR; 084 } 085 086 @Override 087 public void setName(String str) { 088 name = str; 089 } 090 @Override 091 public void acquire() { 092 RVMThread me = RVMThread.getCurrentThread(); 093 Offset offset=Entrypoints.lockStateField.getOffset(); 094 boolean acquired=false; 095 for (int i=0; me.isOnQueue() || i < SPIN_LIMIT;++i) { 096 int oldState=Magic.prepareInt(this,offset); 097 // NOTE: we could be smart here and break out of the spin if we see 098 // that the state is CLEAR_QUEUED or LOCKED_QUEUED, or we could even 099 // check the queue directly and see if there is anything on it; this 100 // would make the lock slightly more fair. 101 if ((oldState==CLEAR && 102 Magic.attemptInt(this,offset,CLEAR,LOCKED)) || 103 (oldState==CLEAR_QUEUED && 104 Magic.attemptInt(this,offset,CLEAR_QUEUED,LOCKED_QUEUED))) { 105 acquired=true; 106 break; 107 } 108 } 109 if (!acquired) { 110 for (;;) { 111 int oldState=Magic.prepareInt(this,offset); 112 if ((oldState==CLEAR && 113 Magic.attemptInt(this,offset,CLEAR,LOCKED)) || 114 (oldState==CLEAR_QUEUED && 115 Magic.attemptInt(this,offset,CLEAR_QUEUED,LOCKED_QUEUED))) { 116 break; 117 } else if ((oldState==LOCKED && 118 Magic.attemptInt(this,offset,LOCKED,QUEUEING)) || 119 (oldState==LOCKED_QUEUED && 120 Magic.attemptInt(this,offset,LOCKED_QUEUED,QUEUEING))) { 121 queue.enqueue(me); 122 Magic.sync(); 123 state=LOCKED_QUEUED; 124 me.monitor().lockNoHandshake(); 125 while (queue.isQueued(me)) { 126 // use await instead of waitNicely because this is NOT a GC point! 127 me.monitor().waitNoHandshake(); 128 } 129 me.monitor().unlock(); 130 } 131 } 132 } 133 thread = me; 134 where = -1; 135 Magic.isync(); 136 } 137 138 @Override 139 public void check(int w) { 140 if (VM.VerifyAssertions) VM._assert(RVMThread.getCurrentThread() == thread); 141 where = w; 142 } 143 144 @Override 145 public void release() { 146 where=-1; 147 thread=null; 148 Magic.sync(); 149 Offset offset=Entrypoints.lockStateField.getOffset(); 150 for (;;) { 151 int oldState=Magic.prepareInt(this,offset); 152 if (VM.VerifyAssertions) VM._assert(oldState==LOCKED || 153 oldState==LOCKED_QUEUED || 154 oldState==QUEUEING); 155 if (oldState==LOCKED && 156 Magic.attemptInt(this,offset,LOCKED,CLEAR)) { 157 break; 158 } else if (oldState==LOCKED_QUEUED && 159 Magic.attemptInt(this,offset,LOCKED_QUEUED,QUEUEING)) { 160 RVMThread toAwaken=queue.dequeue(); 161 if (VM.VerifyAssertions) VM._assert(toAwaken!=null); 162 boolean queueEmpty=queue.isEmpty(); 163 Magic.sync(); 164 if (queueEmpty) { 165 state=CLEAR; 166 } else { 167 state=CLEAR_QUEUED; 168 } 169 toAwaken.monitor().lockedBroadcastNoHandshake(); 170 break; 171 } 172 } 173 } 174 }