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}