/*
* 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 org.jikesrvm.mm.mminterface.Barriers;
import org.jikesrvm.runtime.Magic;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Word;
import org.vmmagic.unboxed.Offset;
/**
* Class to provide synchronization methods where java language
* synchronization is insufficient and Magic.prepare and Magic.attempt
* are at too low a level.
*/
@Uninterruptible
public class Synchronization {
/**
* Atomically swap test value to new value in the specified object and the specified field
* @param base object containing field
* @param offset position of field
* @param testValue expected value of field
* @param newValue new value of field
* @return {@code true}=> successful swap, {@code false}=> field not equal to testValue
*/
@Inline
public static boolean tryCompareAndSwap(Object base, Offset offset, int testValue, int newValue) {
if (Barriers.NEEDS_INT_PUTFIELD_BARRIER || Barriers.NEEDS_INT_GETFIELD_BARRIER) {
return Barriers.intTryCompareAndSwap(base, offset, testValue, newValue);
} else {
if (VM.BuildForIA32) {
return Magic.attemptInt(base, offset, testValue, newValue);
} else {
int oldValue;
do {
oldValue = Magic.prepareInt(base, offset);
if (oldValue != testValue) return false;
} while (!Magic.attemptInt(base, offset, oldValue, newValue));
return true;
}
}
}
/**
* Atomically swap test value to new value in the specified object and the specified field
* @param base object containing field
* @param offset position of field
* @param testValue expected value of field
* @param newValue new value of field
* @return {@code true}=> successful swap, {@code false}=> field not equal to testValue
*/
@Inline
public static boolean tryCompareAndSwap(Object base, Offset offset, long testValue, long newValue) {
if (Barriers.NEEDS_LONG_PUTFIELD_BARRIER || Barriers.NEEDS_LONG_GETFIELD_BARRIER) {
return Barriers.longTryCompareAndSwap(base, offset, testValue, newValue);
} else {
if (VM.BuildForIA32) {
return Magic.attemptLong(base, offset, testValue, newValue);
} else {
long oldValue;
do {
oldValue = Magic.prepareLong(base, offset);
if (oldValue != testValue) return false;
} while (!Magic.attemptLong(base, offset, oldValue, newValue));
return true;
}
}
}
/**
* Atomically swap test value to new value in the specified object and the specified field
* @param base object containing field
* @param offset position of field
* @param testValue expected value of field
* @param newValue new value of field
* @return {@code true}=> successful swap, {@code false}=> field not equal to testValue
*/
@Inline
public static boolean tryCompareAndSwap(Object base, Offset offset, Word testValue, Word newValue) {
if (Barriers.NEEDS_WORD_PUTFIELD_BARRIER || Barriers.NEEDS_WORD_GETFIELD_BARRIER) {
return Barriers.wordTryCompareAndSwap(base, offset, testValue, newValue);
} else {
if (VM.BuildForIA32) {
return Magic.attemptWord(base, offset, testValue, newValue);
} else {
Word oldValue;
do {
oldValue = Magic.prepareWord(base, offset);
if (oldValue.NE(testValue)) return false;
} while (!Magic.attemptWord(base, offset, oldValue, newValue));
return true;
}
}
}
/**
* Atomically swap test value to new value in the specified object and the specified field
* @param base object containing field
* @param offset position of field
* @param testValue expected value of field
* @param newValue new value of field
* @return {@code true}=> successful swap, {@code false}=> field not equal to testValue
*/
@Inline
public static boolean tryCompareAndSwap(Object base, Offset offset, Address testValue, Address newValue) {
if (Barriers.NEEDS_ADDRESS_PUTFIELD_BARRIER || Barriers.NEEDS_ADDRESS_GETFIELD_BARRIER) {
return Barriers.addressTryCompareAndSwap(base, offset, testValue, newValue);
} else {
if (VM.BuildForIA32) {
return Magic.attemptAddress(base, offset, testValue, newValue);
} else {
Address oldValue;
do {
oldValue = Magic.prepareAddress(base, offset);
if (oldValue.NE(testValue))
return false;
} while (!Magic.attemptAddress(base, offset, oldValue, newValue));
return true;
}
}
}
/**
* Atomically swap test value to new value in the specified object and the specified field
* @param base object containing field
* @param offset position of field
* @param testValue expected value of field
* @param newValue new value of field
* @return {@code true}=> successful swap, {@code false}=> field not equal to testValue
*/
@Inline
public static boolean tryCompareAndSwap(Object base, Offset offset, Object testValue, Object newValue) {
if (Barriers.NEEDS_OBJECT_PUTFIELD_BARRIER || Barriers.NEEDS_OBJECT_GETFIELD_BARRIER) {
return Barriers.objectTryCompareAndSwap(base, offset, testValue, newValue);
} else {
if (VM.BuildForIA32) {
return Magic.attemptObject(base, offset, testValue, newValue);
} else {
Object oldValue;
do {
oldValue = Magic.prepareObject(base, offset);
if (oldValue != testValue) return false;
} while (!Magic.attemptObject(base, offset, oldValue, newValue));
return true;
}
}
}
@Inline
public static boolean testAndSet(Object base, Offset offset, int newValue) {
return tryCompareAndSwap(base, offset, 0, newValue);
}
@Inline
public static int fetchAndStore(Object base, Offset offset, int newValue) {
int oldValue;
do {
if (Barriers.NEEDS_INT_GETFIELD_BARRIER) {
oldValue = Barriers.intFieldRead(base, offset, 0);
} else {
oldValue = Magic.getIntAtOffset(base, offset);
}
} while (!tryCompareAndSwap(base, offset, oldValue, newValue));
return oldValue;
}
@Inline
public static Address fetchAndStoreAddress(Object base, Offset offset, Address newValue) {
Address oldValue;
do {
if (Barriers.NEEDS_ADDRESS_GETFIELD_BARRIER) {
oldValue = Barriers.addressFieldRead(base, offset, 0);
} else {
oldValue = Magic.getAddressAtOffset(base, offset);
}
} while (!tryCompareAndSwap(base, offset, oldValue, newValue));
return oldValue;
}
@Inline
public static int fetchAndAdd(Object base, Offset offset, int increment) {
int oldValue;
do {
if (Barriers.NEEDS_INT_GETFIELD_BARRIER) {
oldValue = Barriers.intFieldRead(base, offset, 0);
} else {
oldValue = Magic.getIntAtOffset(base, offset);
}
} while (!tryCompareAndSwap(base, offset, oldValue, oldValue + increment));
return oldValue;
}
@Inline
public static int fetchAndDecrement(Object base, Offset offset, int decrement) {
int oldValue;
do {
if (Barriers.NEEDS_INT_GETFIELD_BARRIER) {
oldValue = Barriers.intFieldRead(base, offset, 0);
} else {
oldValue = Magic.getIntAtOffset(base, offset);
}
} while (!tryCompareAndSwap(base, offset, oldValue, oldValue - decrement));
return oldValue;
}
@Inline
public static Address fetchAndAddAddressWithBound(Object base, Offset offset, int increment, Address bound) {
Address oldValue, newValue;
if (VM.VerifyAssertions) VM._assert(increment > 0);
do {
if (Barriers.NEEDS_ADDRESS_GETFIELD_BARRIER) {
oldValue = Barriers.addressFieldRead(base, offset, 0);
} else {
oldValue = Magic.getAddressAtOffset(base, offset);
}
newValue = oldValue.plus(increment);
if (newValue.GT(bound)) return Address.max();
} while (!tryCompareAndSwap(base, offset, oldValue, newValue));
return oldValue;
}
@Inline
public static Address fetchAndSubAddressWithBound(Object base, Offset offset, int decrement, Address bound) {
Address oldValue, newValue;
if (VM.VerifyAssertions) VM._assert(decrement > 0);
do {
if (Barriers.NEEDS_ADDRESS_GETFIELD_BARRIER) {
oldValue = Barriers.addressFieldRead(base, offset, 0);
} else {
oldValue = Magic.getAddressAtOffset(base, offset);
}
newValue = oldValue.minus(decrement);
if (newValue.LT(bound)) return Address.max();
} while (!tryCompareAndSwap(base, offset, oldValue, newValue));
return oldValue;
}
}