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