1 /** 2 * Copyright 2010 JogAmp Community. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without modification, are 5 * permitted provided that the following conditions are met: 6 * 7 * 1. Redistributions of source code must retain the above copyright notice, this list of 8 * conditions and the following disclaimer. 9 * 10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 * of conditions and the following disclaimer in the documentation and/or other materials 12 * provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * 24 * The views and conclusions contained in the software and documentation are those of the 25 * authors and should not be interpreted as representing official policies, either expressed 26 * or implied, of JogAmp Community. 27 */ 28 29 package jogamp.common.util.locks; 30 31 import java.util.List; 32 import java.util.concurrent.locks.AbstractOwnableSynchronizer; 33 34 import com.jogamp.common.util.locks.RecursiveLock; 35 36 /** 37 * Reentrance locking toolkit, impl a non-complete fair FIFO scheduler. 38 * <p> 39 * Fair scheduling is not guaranteed due to the usage of {@link Object#notify()}, 40 * however new lock-applicants will wait if queue is not empty for {@link #lock()} 41 * and {@link #tryLock(long) tryLock}(timeout>0).</p> 42 * 43 * <p> 44 * Sync object extends {@link AbstractOwnableSynchronizer}, hence monitoring is possible.</p> 45 */ 46 public class RecursiveLockImpl01Unfairish implements RecursiveLock { 47 48 /* package */ static interface Sync { getOwner()49 Thread getOwner(); isOwner(Thread t)50 boolean isOwner(Thread t); setOwner(Thread t)51 void setOwner(Thread t); 52 getLockedStack()53 Throwable getLockedStack(); setLockedStack(Throwable s)54 void setLockedStack(Throwable s); 55 getHoldCount()56 int getHoldCount(); incrHoldCount(Thread t)57 void incrHoldCount(Thread t); decrHoldCount(Thread t)58 void decrHoldCount(Thread t); 59 getQSz()60 int getQSz(); incrQSz()61 void incrQSz(); decrQSz()62 void decrQSz(); 63 } 64 65 @SuppressWarnings("serial") 66 /* package */ static class SingleThreadSync extends AbstractOwnableSynchronizer implements Sync { SingleThreadSync()67 /* package */ SingleThreadSync() { 68 super(); 69 } 70 @Override getOwner()71 public final Thread getOwner() { 72 return getExclusiveOwnerThread(); 73 } 74 @Override isOwner(final Thread t)75 public boolean isOwner(final Thread t) { 76 return getExclusiveOwnerThread()==t; 77 } 78 @Override setOwner(final Thread t)79 public final void setOwner(final Thread t) { 80 setExclusiveOwnerThread(t); 81 } 82 @Override getLockedStack()83 public final Throwable getLockedStack() { 84 return lockedStack; 85 } 86 @Override setLockedStack(final Throwable s)87 public final void setLockedStack(final Throwable s) { 88 final List<Throwable> ls = LockDebugUtil.getRecursiveLockTrace(); 89 if(s==null) { 90 ls.remove(lockedStack); 91 } else { 92 ls.add(s); 93 } 94 lockedStack = s; 95 } 96 @Override getHoldCount()97 public final int getHoldCount() { return holdCount; } 98 @Override incrHoldCount(final Thread t)99 public void incrHoldCount(final Thread t) { holdCount++; } 100 @Override decrHoldCount(final Thread t)101 public void decrHoldCount(final Thread t) { holdCount--; } 102 103 @Override getQSz()104 public final int getQSz() { return qsz; } 105 @Override incrQSz()106 public final void incrQSz() { qsz++; } 107 @Override decrQSz()108 public final void decrQSz() { qsz--; } 109 110 /** lock count by same thread */ 111 private int holdCount = 0; 112 /** queue size of waiting threads */ 113 private int qsz = 0; 114 /** stack trace of the lock, only used if DEBUG */ 115 private Throwable lockedStack = null; 116 } 117 118 protected final Sync sync; 119 RecursiveLockImpl01Unfairish(final Sync sync)120 public RecursiveLockImpl01Unfairish(final Sync sync) { 121 this.sync = sync; 122 } 123 RecursiveLockImpl01Unfairish()124 public RecursiveLockImpl01Unfairish() { 125 this(new SingleThreadSync()); 126 } 127 128 /** 129 * Returns the Throwable instance generated when this lock was taken the 1st time 130 * and if {@link com.jogamp.common.util.locks.Lock#DEBUG} is turned on, otherwise it returns always <code>null</code>. 131 * @see com.jogamp.common.util.locks.Lock#DEBUG 132 */ getLockedStack()133 public final Throwable getLockedStack() { 134 synchronized(sync) { 135 return sync.getLockedStack(); 136 } 137 } 138 139 @Override getOwner()140 public final Thread getOwner() { 141 synchronized(sync) { 142 return sync.getOwner(); 143 } 144 } 145 146 @Override isOwner(final Thread thread)147 public final boolean isOwner(final Thread thread) { 148 synchronized(sync) { 149 return sync.isOwner(thread); 150 } 151 } 152 153 @Override isLocked()154 public final boolean isLocked() { 155 synchronized(sync) { 156 return null != sync.getOwner(); 157 } 158 } 159 160 @Override isLockedByOtherThread()161 public final boolean isLockedByOtherThread() { 162 synchronized(sync) { 163 final Thread o = sync.getOwner(); 164 return null != o && Thread.currentThread() != o ; 165 } 166 } 167 168 @Override getHoldCount()169 public final int getHoldCount() { 170 synchronized(sync) { 171 return sync.getHoldCount(); 172 } 173 } 174 175 @Override validateLocked()176 public final void validateLocked() throws RuntimeException { 177 synchronized(sync) { 178 if ( !sync.isOwner(Thread.currentThread()) ) { 179 if ( null == sync.getOwner() ) { 180 throw new RuntimeException(threadName(Thread.currentThread())+": Not locked: "+toString()); 181 } 182 if(null!=sync.getLockedStack()) { 183 sync.getLockedStack().printStackTrace(); 184 } 185 throw new RuntimeException(Thread.currentThread()+": Not owner: "+toString()); 186 } 187 } 188 } 189 190 @Override lock()191 public final void lock() { 192 synchronized(sync) { 193 try { 194 if(!tryLock(TIMEOUT)) { 195 if(null!=sync.getLockedStack()) { 196 sync.getLockedStack().printStackTrace(); 197 } 198 throw new RuntimeException("Waited "+TIMEOUT+"ms for: "+toString()+" - "+threadName(Thread.currentThread())); 199 } 200 } catch (final InterruptedException e) { 201 throw new RuntimeException("Interrupted", e); 202 } 203 } 204 } 205 206 @Override tryLock(long timeout)207 public final boolean tryLock(long timeout) throws InterruptedException { 208 synchronized(sync) { 209 final Thread cur = Thread.currentThread(); 210 if(TRACE_LOCK) { 211 System.err.println("+++ LOCK 0 "+toString()+", timeout "+timeout+" ms, cur "+threadName(cur)); 212 } 213 if (sync.isOwner(cur)) { 214 sync.incrHoldCount(cur); 215 if(TRACE_LOCK) { 216 System.err.println("+++ LOCK XR "+toString()+", cur "+threadName(cur)); 217 } 218 return true; 219 } 220 221 if ( sync.getOwner() != null || ( 0<timeout && 0<sync.getQSz() ) ) { 222 223 if ( 0 >= timeout ) { 224 // locked by other thread and no waiting requested 225 if(TRACE_LOCK) { 226 System.err.println("+++ LOCK XY "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms"); 227 } 228 return false; 229 } 230 231 sync.incrQSz(); 232 do { 233 final long t0 = System.currentTimeMillis(); 234 sync.wait(timeout); 235 timeout -= System.currentTimeMillis() - t0; 236 } while (null != sync.getOwner() && 0 < timeout) ; 237 sync.decrQSz(); 238 239 if( 0 >= timeout && sync.getOwner() != null ) { 240 // timed out 241 if(TRACE_LOCK) { 242 System.err.println("+++ LOCK XX "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms"); 243 } 244 return false; 245 } 246 247 if(TRACE_LOCK) { 248 System.err.println("+++ LOCK X1 "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms"); 249 } 250 } else if(TRACE_LOCK) { 251 System.err.println("+++ LOCK X0 "+toString()+", cur "+threadName(cur)); 252 } 253 254 sync.setOwner(cur); 255 sync.incrHoldCount(cur); 256 257 if(DEBUG) { 258 sync.setLockedStack(new Throwable("Previously locked by "+toString())); 259 } 260 return true; 261 } 262 } 263 264 265 @Override unlock()266 public final void unlock() { 267 synchronized(sync) { 268 unlock(null); 269 } 270 } 271 272 @Override unlock(final Runnable taskAfterUnlockBeforeNotify)273 public void unlock(final Runnable taskAfterUnlockBeforeNotify) { 274 synchronized(sync) { 275 validateLocked(); 276 final Thread cur = Thread.currentThread(); 277 278 sync.decrHoldCount(cur); 279 280 if (sync.getHoldCount() > 0) { 281 if(TRACE_LOCK) { 282 System.err.println("--- LOCK XR "+toString()+", cur "+threadName(cur)); 283 } 284 return; 285 } 286 287 sync.setOwner(null); 288 if(DEBUG) { 289 sync.setLockedStack(null); 290 } 291 if(null!=taskAfterUnlockBeforeNotify) { 292 taskAfterUnlockBeforeNotify.run(); 293 } 294 295 if(TRACE_LOCK) { 296 System.err.println("--- LOCK X0 "+toString()+", cur "+threadName(cur)+", signal any"); 297 } 298 sync.notify(); 299 } 300 } 301 302 @Override getQueueLength()303 public final int getQueueLength() { 304 synchronized(sync) { 305 return sync.getQSz(); 306 } 307 } 308 309 @Override toString()310 public String toString() { 311 return syncName()+"[count "+sync.getHoldCount()+ 312 ", qsz "+sync.getQSz()+", owner "+threadName(sync.getOwner())+"]"; 313 } 314 syncName()315 /* package */ final String syncName() { 316 return "<"+Integer.toHexString(this.hashCode())+", "+Integer.toHexString(sync.hashCode())+">"; 317 } threadName(final Thread t)318 /* package */ final String threadName(final Thread t) { return null!=t ? "<"+t.getName()+">" : "<NULL>" ; } 319 } 320 321