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    }