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 org.jikesrvm.mm.mminterface.Barriers;
017import org.jikesrvm.runtime.Magic;
018import org.vmmagic.pragma.Inline;
019import org.vmmagic.pragma.Uninterruptible;
020import org.vmmagic.unboxed.Address;
021import org.vmmagic.unboxed.Word;
022import org.vmmagic.unboxed.Offset;
023
024/**
025 * Class to provide synchronization methods where java language
026 * synchronization is insufficient and Magic.prepare and Magic.attempt
027 * are at too low a level.
028 */
029@Uninterruptible
030public class Synchronization {
031
032  /**
033   * Atomically swap test value to new value in the specified object and the specified field
034   * @param base object containing field
035   * @param offset position of field
036   * @param testValue expected value of field
037   * @param newValue new value of field
038   * @return {@code true}=> successful swap, {@code false}=> field not equal to testValue
039   */
040  @Inline
041  public static boolean tryCompareAndSwap(Object base, Offset offset, int testValue, int newValue) {
042    if (Barriers.NEEDS_INT_PUTFIELD_BARRIER || Barriers.NEEDS_INT_GETFIELD_BARRIER) {
043      return Barriers.intTryCompareAndSwap(base, offset, testValue, newValue);
044    } else {
045      if (VM.BuildForIA32) {
046        return Magic.attemptInt(base, offset, testValue, newValue);
047      } else {
048        int oldValue;
049        do {
050          oldValue = Magic.prepareInt(base, offset);
051          if (oldValue != testValue) return false;
052        } while (!Magic.attemptInt(base, offset, oldValue, newValue));
053        return true;
054      }
055    }
056  }
057
058  /**
059   * Atomically swap test value to new value in the specified object and the specified field
060   * @param base object containing field
061   * @param offset position of field
062   * @param testValue expected value of field
063   * @param newValue new value of field
064   * @return {@code true}=> successful swap, {@code false}=> field not equal to testValue
065   */
066  @Inline
067  public static boolean tryCompareAndSwap(Object base, Offset offset, long testValue, long newValue) {
068    if (Barriers.NEEDS_LONG_PUTFIELD_BARRIER || Barriers.NEEDS_LONG_GETFIELD_BARRIER) {
069      return Barriers.longTryCompareAndSwap(base, offset, testValue, newValue);
070    } else {
071      if (VM.BuildForIA32) {
072        return Magic.attemptLong(base, offset, testValue, newValue);
073      } else {
074        long oldValue;
075        do {
076          oldValue = Magic.prepareLong(base, offset);
077          if (oldValue != testValue) return false;
078        } while (!Magic.attemptLong(base, offset, oldValue, newValue));
079        return true;
080      }
081    }
082  }
083
084  /**
085   * Atomically swap test value to new value in the specified object and the specified field
086   * @param base object containing field
087   * @param offset position of field
088   * @param testValue expected value of field
089   * @param newValue new value of field
090   * @return {@code true}=> successful swap, {@code false}=> field not equal to testValue
091   */
092  @Inline
093  public static boolean tryCompareAndSwap(Object base, Offset offset, Word testValue, Word newValue) {
094    if (Barriers.NEEDS_WORD_PUTFIELD_BARRIER || Barriers.NEEDS_WORD_GETFIELD_BARRIER) {
095      return Barriers.wordTryCompareAndSwap(base, offset, testValue, newValue);
096    } else {
097      if (VM.BuildForIA32) {
098        return Magic.attemptWord(base, offset, testValue, newValue);
099      } else {
100        Word oldValue;
101        do {
102          oldValue = Magic.prepareWord(base, offset);
103          if (oldValue.NE(testValue)) return false;
104        } while (!Magic.attemptWord(base, offset, oldValue, newValue));
105        return true;
106      }
107    }
108  }
109
110  /**
111   * Atomically swap test value to new value in the specified object and the specified field
112   * @param base object containing field
113   * @param offset position of field
114   * @param testValue expected value of field
115   * @param newValue new value of field
116   * @return {@code true}=> successful swap, {@code false}=> field not equal to testValue
117   */
118  @Inline
119  public static boolean tryCompareAndSwap(Object base, Offset offset, Address testValue, Address newValue) {
120    if (Barriers.NEEDS_ADDRESS_PUTFIELD_BARRIER || Barriers.NEEDS_ADDRESS_GETFIELD_BARRIER) {
121      return Barriers.addressTryCompareAndSwap(base, offset, testValue, newValue);
122    } else {
123      if (VM.BuildForIA32) {
124        return Magic.attemptAddress(base, offset, testValue, newValue);
125      } else {
126        Address oldValue;
127        do {
128          oldValue = Magic.prepareAddress(base, offset);
129          if (oldValue.NE(testValue))
130            return false;
131        } while (!Magic.attemptAddress(base, offset, oldValue, newValue));
132        return true;
133      }
134    }
135  }
136
137  /**
138   * Atomically swap test value to new value in the specified object and the specified field
139   * @param base object containing field
140   * @param offset position of field
141   * @param testValue expected value of field
142   * @param newValue new value of field
143   * @return {@code true}=> successful swap, {@code false}=> field not equal to testValue
144   */
145  @Inline
146  public static boolean tryCompareAndSwap(Object base, Offset offset, Object testValue, Object newValue) {
147    if (Barriers.NEEDS_OBJECT_PUTFIELD_BARRIER || Barriers.NEEDS_OBJECT_GETFIELD_BARRIER) {
148      return Barriers.objectTryCompareAndSwap(base, offset, testValue, newValue);
149    } else {
150      if (VM.BuildForIA32) {
151        return Magic.attemptObject(base, offset, testValue, newValue);
152      } else {
153        Object oldValue;
154        do {
155          oldValue = Magic.prepareObject(base, offset);
156          if (oldValue != testValue) return false;
157        } while (!Magic.attemptObject(base, offset, oldValue, newValue));
158        return true;
159      }
160    }
161  }
162
163  @Inline
164  public static boolean testAndSet(Object base, Offset offset, int newValue) {
165    return tryCompareAndSwap(base, offset, 0, newValue);
166  }
167
168  @Inline
169  public static int fetchAndStore(Object base, Offset offset, int newValue) {
170    int oldValue;
171    do {
172      if (Barriers.NEEDS_INT_GETFIELD_BARRIER) {
173        oldValue = Barriers.intFieldRead(base, offset, 0);
174      } else {
175        oldValue = Magic.getIntAtOffset(base, offset);
176      }
177    } while (!tryCompareAndSwap(base, offset, oldValue, newValue));
178    return oldValue;
179  }
180
181  @Inline
182  public static Address fetchAndStoreAddress(Object base, Offset offset, Address newValue) {
183    Address oldValue;
184    do {
185      if (Barriers.NEEDS_ADDRESS_GETFIELD_BARRIER) {
186        oldValue = Barriers.addressFieldRead(base, offset, 0);
187      } else {
188        oldValue = Magic.getAddressAtOffset(base, offset);
189      }
190    } while (!tryCompareAndSwap(base, offset, oldValue, newValue));
191    return oldValue;
192  }
193
194  @Inline
195  public static int fetchAndAdd(Object base, Offset offset, int increment) {
196    int oldValue;
197    do {
198      if (Barriers.NEEDS_INT_GETFIELD_BARRIER) {
199        oldValue = Barriers.intFieldRead(base, offset, 0);
200      } else {
201        oldValue = Magic.getIntAtOffset(base, offset);
202      }
203    } while (!tryCompareAndSwap(base, offset, oldValue, oldValue + increment));
204    return oldValue;
205  }
206
207  @Inline
208  public static int fetchAndDecrement(Object base, Offset offset, int decrement) {
209    int oldValue;
210    do {
211      if (Barriers.NEEDS_INT_GETFIELD_BARRIER) {
212        oldValue = Barriers.intFieldRead(base, offset, 0);
213      } else {
214        oldValue = Magic.getIntAtOffset(base, offset);
215      }
216    } while (!tryCompareAndSwap(base, offset, oldValue, oldValue - decrement));
217    return oldValue;
218  }
219
220  @Inline
221  public static Address fetchAndAddAddressWithBound(Object base, Offset offset, int increment, Address bound) {
222    Address oldValue, newValue;
223    if (VM.VerifyAssertions) VM._assert(increment > 0);
224    do {
225      if (Barriers.NEEDS_ADDRESS_GETFIELD_BARRIER) {
226        oldValue = Barriers.addressFieldRead(base, offset, 0);
227      } else {
228        oldValue = Magic.getAddressAtOffset(base, offset);
229      }
230      newValue = oldValue.plus(increment);
231      if (newValue.GT(bound)) return Address.max();
232    } while (!tryCompareAndSwap(base, offset, oldValue, newValue));
233    return oldValue;
234  }
235
236  @Inline
237  public static Address fetchAndSubAddressWithBound(Object base, Offset offset, int decrement, Address bound) {
238    Address oldValue, newValue;
239    if (VM.VerifyAssertions) VM._assert(decrement > 0);
240    do {
241      if (Barriers.NEEDS_ADDRESS_GETFIELD_BARRIER) {
242        oldValue = Barriers.addressFieldRead(base, offset, 0);
243      } else {
244        oldValue = Magic.getAddressAtOffset(base, offset);
245      }
246      newValue = oldValue.minus(decrement);
247      if (newValue.LT(bound)) return Address.max();
248    } while (!tryCompareAndSwap(base, offset, oldValue, newValue));
249    return oldValue;
250  }
251}