/*
* Copyright 2016 higherfrequencytrading.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.openhft.lang.locks;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.nio.ByteOrder.nativeOrder;
import static net.openhft.lang.io.AbstractBytes.UNSIGNED_INT_MASK;
public final class VanillaReadWriteUpdateWithWaitsLockingStrategy
extends AbstractReadWriteLockingStrategy
implements ReadWriteUpdateWithWaitsLockingStrategy {
static final long COUNT_WORD_OFFSET = 0L;
static final long WAIT_WORD_OFFSET = COUNT_WORD_OFFSET + 4L;
static final int COUNT_WORD_SHIFT = nativeOrder() == LITTLE_ENDIAN ? 0 : 32;
static final int WAIT_WORD_SHIFT = nativeOrder() == LITTLE_ENDIAN ? 32 : 0;
static final int READ_BITS = 30;
static final int MAX_READ = (1 << READ_BITS) - 1;
static final int READ_MASK = MAX_READ;
static final int READ_PARTY = 1;
static final int UPDATE_PARTY = 1 << READ_BITS;
static final int WRITE_LOCKED_COUNT_WORD = UPDATE_PARTY << 1;
static final int MAX_WAIT = Integer.MAX_VALUE;
static final int WAIT_PARTY = 1;
private static final ReadWriteUpdateWithWaitsLockingStrategy INSTANCE =
new VanillaReadWriteUpdateWithWaitsLockingStrategy();
private VanillaReadWriteUpdateWithWaitsLockingStrategy() {
}
public static ReadWriteUpdateWithWaitsLockingStrategy instance() {
return INSTANCE;
}
private static <T> long getLockWord(NativeAtomicAccess<T> access, T t, long offset) {
return access.getLongVolatile(t, offset);
}
private static <T> boolean casLockWord(
NativeAtomicAccess<T> access, T t, long offset, long expected, long x) {
return access.compareAndSwapLong(t, offset, expected, x);
}
private static int countWord(long lockWord) {
return (int) (lockWord >> COUNT_WORD_SHIFT);
}
private static int waitWord(long lockWord) {
return (int) (lockWord >> WAIT_WORD_SHIFT);
}
private static long lockWord(int countWord, int waitWord) {
return ((((long) countWord) & UNSIGNED_INT_MASK) << COUNT_WORD_SHIFT) |
((((long) waitWord) & UNSIGNED_INT_MASK) << WAIT_WORD_SHIFT);
}
private static <T> int getCountWord(NativeAtomicAccess<T> access, T t, long offset) {
return access.getIntVolatile(t, offset + COUNT_WORD_OFFSET);
}
private static <T> boolean casCountWord(
NativeAtomicAccess<T> access, T t, long offset, int expected, int x) {
return access.compareAndSwapInt(t, offset + COUNT_WORD_OFFSET, expected, x);
}
private static <T> void putCountWord(
NativeAtomicAccess<T> access, T t, long offset, int countWord) {
access.putOrderedInt(t, offset + COUNT_WORD_OFFSET, countWord);
}
private static boolean writeLocked(int countWord) {
return countWord == WRITE_LOCKED_COUNT_WORD;
}
private static void checkWriteLocked(int countWord) {
if (countWord != WRITE_LOCKED_COUNT_WORD)
throw new IllegalMonitorStateException("Expected write lock");
}
private static boolean updateLocked(int countWord) {
return (countWord & UPDATE_PARTY) != 0;
}
private static void checkUpdateLocked(int countWord) {
if (!updateLocked(countWord))
throw new IllegalMonitorStateException("Expected update lock");
}
private static int readCount(int countWord) {
return countWord & READ_MASK;
}
private static void checkReadLocked(int countWord) {
if (readCount(countWord) <= 0)
throw new IllegalMonitorStateException("Expected read lock");
}
private static void checkReadCountForIncrement(int countWord) {
if (readCount(countWord) == MAX_READ) {
throw new IllegalMonitorStateException(
"Lock count reached the limit of " + MAX_READ);
}
}
private static <T> int getWaitWord(NativeAtomicAccess<T> access, T t, long offset) {
return access.getIntVolatile(t, offset + WAIT_WORD_OFFSET);
}
private static <T> boolean casWaitWord(
NativeAtomicAccess<T> access, T t, long offset, int expected, int x) {
return access.compareAndSwapInt(t, offset + WAIT_WORD_OFFSET, expected, x);
}
private static void checkWaitWordForIncrement(int waitWord) {
if (waitWord == MAX_WAIT) {
throw new IllegalMonitorStateException(
"Wait count reached the limit of " + MAX_WAIT);
}
}
private static void checkWaitWordForDecrement(int waitWord) {
if (waitWord == 0) {
throw new IllegalMonitorStateException(
"Wait count underflowed");
}
}
private static <T> boolean tryWriteLockAndDeregisterWait0(
NativeAtomicAccess<T> access, T t, long offset, long lockWord) {
int waitWord = waitWord(lockWord);
checkWaitWordForDecrement(waitWord);
return casLockWord(access, t, offset, lockWord,
lockWord(WRITE_LOCKED_COUNT_WORD, waitWord - WAIT_PARTY));
}
private static boolean checkExclusiveUpdateLocked(int countWord) {
checkUpdateLocked(countWord);
return countWord == UPDATE_PARTY;
}
private static <T> void checkWriteLockedAndPut(
NativeAtomicAccess<T> access, T t, long offset, int countWord) {
checkWriteLocked(getCountWord(access, t, offset));
putCountWord(access, t, offset, countWord);
}
@Override
public long resetState() {
return 0L;
}
@Override
public <T> void reset(NativeAtomicAccess<T> access, T t, long offset) {
access.putOrderedLong(t, offset, 0L);
}
@Override
public <T> void resetKeepingWaits(NativeAtomicAccess<T> access, T t, long offset) {
putCountWord(access, t, offset, 0);
}
@Override
public <T> boolean tryReadLock(NativeAtomicAccess<T> access, T t, long offset) {
long lockWord = getLockWord(access, t, offset);
int countWord = countWord(lockWord);
if (!writeLocked(countWord) && waitWord(lockWord) == 0) {
checkReadCountForIncrement(countWord);
if (casCountWord(access, t, offset, countWord, countWord + READ_PARTY))
return true;
}
return false;
}
@Override
public <T> boolean tryUpgradeReadToUpdateLock(NativeAtomicAccess<T> access, T t, long offset) {
int countWord = getCountWord(access, t, offset);
checkReadLocked(countWord);
return !updateLocked(countWord) &&
casCountWord(access, t, offset, countWord, countWord - READ_PARTY + UPDATE_PARTY);
}
@Override
public <T> boolean tryUpgradeReadToWriteLock(NativeAtomicAccess<T> access, T t, long offset) {
int countWord = getCountWord(access, t, offset);
checkReadLocked(countWord);
return countWord == READ_PARTY &&
casCountWord(access, t, offset, READ_PARTY, WRITE_LOCKED_COUNT_WORD);
}
@Override
public <T> boolean tryUpgradeReadToWriteLockAndDeregisterWait(
NativeAtomicAccess<T> access, T t, long offset) {
long lockWord = getLockWord(access, t, offset);
int countWord = countWord(lockWord);
checkReadLocked(countWord);
return countWord == READ_PARTY &&
tryWriteLockAndDeregisterWait0(access, t, offset, lockWord);
}
@Override
public <T> boolean tryUpdateLock(NativeAtomicAccess<T> access, T t, long offset) {
long lockWord = getLockWord(access, t, offset);
int countWord = countWord(lockWord);
if (!updateLocked(countWord) && !writeLocked(countWord) && waitWord(lockWord) == 0) {
if (casCountWord(access, t, offset, countWord, countWord + UPDATE_PARTY))
return true;
}
return false;
}
@Override
public <T> boolean tryWriteLock(NativeAtomicAccess<T> access, T t, long offset) {
return getCountWord(access, t, offset) == 0 &&
casCountWord(access, t, offset, 0, WRITE_LOCKED_COUNT_WORD);
}
@Override
public <T> boolean tryWriteLockAndDeregisterWait(
NativeAtomicAccess<T> access, T t, long offset) {
long lockWord = getLockWord(access, t, offset);
int countWord = countWord(lockWord);
return countWord == 0 && tryWriteLockAndDeregisterWait0(access, t, offset, lockWord);
}
@Override
public <T> void registerWait(NativeAtomicAccess<T> access, T t, long offset) {
while (true) {
int waitWord = getWaitWord(access, t, offset);
checkWaitWordForIncrement(waitWord);
if (casWaitWord(access, t, offset, waitWord, waitWord + WAIT_PARTY))
return;
}
}
@Override
public <T> void deregisterWait(NativeAtomicAccess<T> access, T t, long offset) {
while (true) {
int waitWord = getWaitWord(access, t, offset);
checkWaitWordForDecrement(waitWord);
if (casWaitWord(access, t, offset, waitWord, waitWord - WAIT_PARTY))
return;
}
}
@Override
public <T> boolean tryUpgradeUpdateToWriteLock(NativeAtomicAccess<T> access, T t, long offset) {
int countWord = getCountWord(access, t, offset);
return checkExclusiveUpdateLocked(countWord) &&
casCountWord(access, t, offset, countWord, WRITE_LOCKED_COUNT_WORD);
}
@Override
public <T> boolean tryUpgradeUpdateToWriteLockAndDeregisterWait(
NativeAtomicAccess<T> access, T t, long offset) {
long lockWord = getLockWord(access, t, offset);
int countWord = countWord(lockWord);
return checkExclusiveUpdateLocked(countWord) &&
tryWriteLockAndDeregisterWait0(access, t, offset, lockWord);
}
@Override
public <T> void readUnlock(NativeAtomicAccess<T> access, T t, long offset) {
while (true) {
int countWord = getCountWord(access, t, offset);
checkReadLocked(countWord);
if (casCountWord(access, t, offset, countWord, countWord - READ_PARTY))
return;
}
}
@Override
public <T> void updateUnlock(NativeAtomicAccess<T> access, T t, long offset) {
while (true) {
int countWord = getCountWord(access, t, offset);
checkUpdateLocked(countWord);
if (casCountWord(access, t, offset, countWord, countWord - UPDATE_PARTY)) {
return;
}
}
}
@Override
public <T> void downgradeUpdateToReadLock(NativeAtomicAccess<T> access, T t, long offset) {
while (true) {
int countWord = getCountWord(access, t, offset);
checkUpdateLocked(countWord);
checkReadCountForIncrement(countWord);
if (casCountWord(access, t, offset, countWord, countWord - UPDATE_PARTY + READ_PARTY)) {
return;
}
}
}
@Override
public <T> void writeUnlock(NativeAtomicAccess<T> access, T t, long offset) {
checkWriteLockedAndPut(access, t, offset, 0);
}
@Override
public <T> void downgradeWriteToUpdateLock(NativeAtomicAccess<T> access, T t, long offset) {
checkWriteLockedAndPut(access, t, offset, UPDATE_PARTY);
}
@Override
public boolean isUpdateLocked(long state) {
return updateLocked(countWord(state));
}
@Override
public <T> void downgradeWriteToReadLock(NativeAtomicAccess<T> access, T t, long offset) {
checkWriteLockedAndPut(access, t, offset, READ_PARTY);
}
@Override
public <T> long getState(NativeAtomicAccess<T> access, T t, long offset) {
return getLockWord(access, t, offset);
}
@Override
public int readLockCount(long state) {
return readCount(countWord(state));
}
@Override
public boolean isWriteLocked(long state) {
return writeLocked(countWord(state));
}
@Override
public int waitCount(long state) {
return waitWord(state);
}
@Override
public boolean isLocked(long state) {
return countWord(state) != 0;
}
@Override
public int lockCount(long state) {
int countWord = countWord(state);
int lockCount = readCount(countWord);
if (lockCount > 0) {
return lockCount + (updateLocked(countWord) ? 1 : 0);
} else {
return writeLocked(countWord) ? 1 : 0;
}
}
@Override
public String toString(long state) {
return "[read locks = " + readLockCount(state) +
", update locked = " + isUpdateLocked(state) +
", write locked = " + isWriteLocked(state) +
", waits = " + waitCount(state) + "]";
}
@Override
public int sizeInBytes() {
return 8;
}
}