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.scheduler; 014 015 import org.jikesrvm.VM; 016 import static org.jikesrvm.runtime.SysCall.sysCall; 017 import org.jikesrvm.runtime.RuntimeEntrypoints; 018 import org.vmmagic.pragma.Uninterruptible; 019 import org.vmmagic.pragma.Interruptible; 020 import org.vmmagic.pragma.Unpreemptible; 021 import org.vmmagic.pragma.Untraced; 022 023 /** 024 * A light-weigh condition variable and lock, like Monitor, but this 025 * one is movable and can be garbage collected. Note that this lock is 026 * heavier than an object monitor, but has the advantage of being usable 027 * within GC (this lock never allocates in its methods, and never uses 028 * read or write barriers, either). 029 */ 030 @Uninterruptible 031 public final class LightMonitor { 032 ThreadQueue waiting; 033 ThreadQueue entering; 034 SpinLock mutex; 035 @Untraced RVMThread holder; 036 int recCount; 037 038 public LightMonitor() { 039 waiting=new ThreadQueue(); 040 entering=new ThreadQueue(); 041 mutex=new SpinLock(); 042 } 043 044 @Unpreemptible 045 public void lockWithHandshake() { 046 RVMThread me=RVMThread.getCurrentThread(); 047 if (holder==me) { 048 recCount++; 049 } else { 050 mutex.lock(); 051 while (holder!=null) { 052 entering.enqueue(me); 053 mutex.unlock(); 054 me.monitor().lockNoHandshake(); 055 while (entering.isQueued(me)) { 056 me.monitor().waitWithHandshake(); 057 } 058 me.monitor().unlock(); 059 mutex.lock(); 060 } 061 holder=me; 062 mutex.unlock(); 063 recCount=1; 064 } 065 } 066 067 public void unlock() { 068 if (recCount>1) { 069 recCount--; 070 } else { 071 if (VM.VerifyAssertions) VM._assert(recCount==1); 072 if (VM.VerifyAssertions) VM._assert(holder==RVMThread.getCurrentThread()); 073 mutex.lock(); 074 RVMThread toAwaken=entering.dequeue(); 075 holder=null; 076 recCount=0; 077 mutex.unlock(); 078 if (toAwaken!=null) { 079 toAwaken.monitor().lockedBroadcastNoHandshake(); 080 } 081 } 082 } 083 084 @Interruptible 085 private void waitImpl(long whenAwake) { 086 if (VM.VerifyAssertions) VM._assert(recCount>=1); 087 if (VM.VerifyAssertions) VM._assert(holder==RVMThread.getCurrentThread()); 088 089 RVMThread me=RVMThread.getCurrentThread(); 090 091 boolean throwInterrupt = false; 092 Throwable throwThis = null; 093 094 mutex.lock(); 095 waiting.enqueue(me); 096 mutex.unlock(); 097 int myRecCount=recCount; 098 recCount=1; 099 unlock(); 100 101 me.monitor().lockNoHandshake(); 102 while (waiting.isQueued(me) && 103 (whenAwake!=0 || sysCall.sysNanoTime() < whenAwake) && 104 !me.hasInterrupt && me.asyncThrowable == null) { 105 if (whenAwake==0) { 106 me.monitor().waitWithHandshake(); 107 } else { 108 me.monitor().timedWaitAbsoluteWithHandshake(whenAwake); 109 } 110 } 111 if (me.hasInterrupt) { 112 throwInterrupt = true; 113 me.hasInterrupt = false; 114 } 115 if (me.asyncThrowable != null) { 116 throwThis = me.asyncThrowable; 117 me.asyncThrowable = null; 118 } 119 me.monitor().unlock(); 120 121 mutex.lock(); 122 waiting.remove(me); 123 mutex.unlock(); 124 125 lockWithHandshake(); 126 recCount=myRecCount; 127 128 // check if we should exit in a special way 129 if (throwThis != null) { 130 RuntimeEntrypoints.athrow(throwThis); 131 } 132 if (throwInterrupt) { 133 RuntimeEntrypoints.athrow(new InterruptedException("sleep interrupted")); 134 } 135 } 136 137 @Interruptible 138 public void waitInterruptibly() { 139 waitImpl(0); 140 } 141 142 @Interruptible 143 public void timedWaitAbsoluteInterruptibly(long whenAwakeNanos) { 144 waitImpl(whenAwakeNanos); 145 } 146 147 @Interruptible 148 public void timedWaitRelativeInterruptibly(long delayNanos) { 149 waitImpl(sysCall.sysNanoTime()+delayNanos); 150 } 151 152 public void broadcast() { 153 for (;;) { 154 mutex.lock(); 155 RVMThread toAwaken=waiting.dequeue(); 156 mutex.unlock(); 157 if (toAwaken==null) break; 158 toAwaken.monitor().lockedBroadcastNoHandshake(); 159 } 160 } 161 162 @Unpreemptible 163 public void lockedBroadcastWithHandshake() { 164 lockWithHandshake(); 165 broadcast(); 166 unlock(); 167 } 168 }