/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.scheduler; import org.jikesrvm.VM; import static org.jikesrvm.runtime.SysCall.sysCall; import org.jikesrvm.runtime.RuntimeEntrypoints; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.pragma.Interruptible; import org.vmmagic.pragma.Unpreemptible; import org.vmmagic.pragma.Untraced; /** * A light-weigh condition variable and lock, like Monitor, but this * one is movable and can be garbage collected. Note that this lock is * heavier than an object monitor, but has the advantage of being usable * within GC (this lock never allocates in its methods, and never uses * read or write barriers, either). */ @Uninterruptible public final class LightMonitor { ThreadQueue waiting; ThreadQueue entering; SpinLock mutex; //NB, this can only be Untraced as RVMThreads are unmoveable @Untraced RVMThread holder; int recCount; public LightMonitor() { waiting = new ThreadQueue(); entering = new ThreadQueue(); mutex = new SpinLock(); } @Unpreemptible public void lockWithHandshake() { RVMThread me = RVMThread.getCurrentThread(); if (holder == me) { recCount++; } else { mutex.lock(); while (holder != null) { entering.enqueue(me); mutex.unlock(); me.monitor().lockNoHandshake(); while (entering.isQueued(me)) { me.monitor().waitWithHandshake(); } me.monitor().unlock(); mutex.lock(); } holder = me; mutex.unlock(); recCount = 1; } } public void unlock() { if (recCount > 1) { recCount--; } else { if (VM.VerifyAssertions) VM._assert(recCount == 1); if (VM.VerifyAssertions) VM._assert(holder == RVMThread.getCurrentThread()); mutex.lock(); RVMThread toAwaken = entering.dequeue(); holder = null; recCount = 0; mutex.unlock(); if (toAwaken != null) { toAwaken.monitor().lockedBroadcastNoHandshake(); } } } @Interruptible private void waitImpl(long whenAwake) { if (VM.VerifyAssertions) VM._assert(recCount >= 1); if (VM.VerifyAssertions) VM._assert(holder == RVMThread.getCurrentThread()); RVMThread me = RVMThread.getCurrentThread(); boolean throwInterrupt = false; Throwable throwThis = null; mutex.lock(); waiting.enqueue(me); mutex.unlock(); int myRecCount = recCount; recCount = 1; unlock(); me.monitor().lockNoHandshake(); while (waiting.isQueued(me) && (whenAwake != 0 || sysCall.sysNanoTime() < whenAwake) && !me.hasInterrupt && me.asyncThrowable == null) { if (whenAwake == 0) { me.monitor().waitWithHandshake(); } else { me.monitor().timedWaitAbsoluteWithHandshake(whenAwake); } } if (me.hasInterrupt) { throwInterrupt = true; me.hasInterrupt = false; } if (me.asyncThrowable != null) { throwThis = me.asyncThrowable; me.asyncThrowable = null; } me.monitor().unlock(); mutex.lock(); waiting.remove(me); mutex.unlock(); lockWithHandshake(); recCount = myRecCount; // check if we should exit in a special way if (throwThis != null) { RuntimeEntrypoints.athrow(throwThis); } if (throwInterrupt) { RuntimeEntrypoints.athrow(new InterruptedException("sleep interrupted")); } } @Interruptible public void waitInterruptibly() { waitImpl(0); } @Interruptible public void timedWaitAbsoluteInterruptibly(long whenAwakeNanos) { waitImpl(whenAwakeNanos); } @Interruptible public void timedWaitRelativeInterruptibly(long delayNanos) { waitImpl(sysCall.sysNanoTime() + delayNanos); } public void broadcast() { for (;;) { mutex.lock(); RVMThread toAwaken = waiting.dequeue(); mutex.unlock(); if (toAwaken == null) break; toAwaken.monitor().lockedBroadcastNoHandshake(); } } @Unpreemptible public void lockedBroadcastWithHandshake() { lockWithHandshake(); broadcast(); unlock(); } }