/*
* Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* (see java/src/trees/lockbased/catreeutils/LICENSE)
*/
package trees.lockbased.catreeutils;
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import sun.misc.Unsafe;
/**
* Objects of this class are used by the CA tree base node
* implementation in "DualLFCASAVLTreeMapSTD.java". Please see that
* file for details. This class implements a sequence lock. Refer to
* <a href="https://www.kernel.org/pub/linux/kernel/people/christoph/gelato/gelato2005-paper.pdf">https://www.kernel.org/pub/linux/kernel/people/christoph/gelato/gelato2005-paper.pdf</a>
* for more information about sequence locks.
*
* @author Kjell Winblad
*/
public class SeqLock {
volatile long seqNumber = 2L;
private int statLockStatistics = 0;
private static final AtomicLongFieldUpdater<SeqLock> seqNumberUpdater =
AtomicLongFieldUpdater.newUpdater(SeqLock.class, "seqNumber");
private static final Unsafe unsafe;
private static final int STAT_LOCK_HIGH_CONTENTION_LIMIT = 1000;
private static final int STAT_LOCK_LOW_CONTENTION_LIMIT = -1000;
private static final int STAT_LOCK_FAILURE_CONTRIB = 250;
private static final int STAT_LOCK_SUCCESS_CONTRIB = 1;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
} catch (Exception ex) {
throw new Error(ex);
}
}
public boolean tryLock() {
long readSeqNumber = seqNumber;
if((readSeqNumber % 2) != 0){
return false;
}else{
boolean success = seqNumberUpdater.compareAndSet(this, readSeqNumber, readSeqNumber + 1);
if(success){
return true;
}else{
return false;
}
}
}
public void lock(){
while(true){
long readSeqNumber = seqNumber;
while((readSeqNumber % 2) != 0){
unsafe.fullFence();
unsafe.fullFence();
readSeqNumber = seqNumber;
}
if(seqNumberUpdater.compareAndSet(this, readSeqNumber, readSeqNumber + 1)){
break;
}
}
}
public void unlock(){
seqNumber = seqNumber + 1;
}
public boolean isWriteLocked(){
return (seqNumber % 2) != 0;
}
public long tryOptimisticRead() {
long readSeqNumber = seqNumber;
if((readSeqNumber % 2) != 0){
return 0;
}else{
return readSeqNumber;
}
}
public boolean validate(long optimisticReadToken) {
unsafe.loadFence();
long readSeqNumber = seqNumber;
return readSeqNumber == optimisticReadToken;
}
public void lockUpdateStatistics(){
if (tryLock()) {
statLockStatistics -= STAT_LOCK_SUCCESS_CONTRIB;
return;
}
lock();
statLockStatistics += STAT_LOCK_FAILURE_CONTRIB;
}
public void addToContentionStatistics(){
statLockStatistics += STAT_LOCK_FAILURE_CONTRIB;
}
public void subFromContentionStatistics(){
statLockStatistics -= STAT_LOCK_SUCCESS_CONTRIB;
}
public int getLockStatistics(){
return statLockStatistics;
}
public void resetStatistics(){
statLockStatistics = 0;
}
public boolean isHighContentionLimitReached(){
return statLockStatistics > STAT_LOCK_HIGH_CONTENTION_LIMIT;
}
public boolean isLowContentionLimitReached(){
return statLockStatistics < STAT_LOCK_LOW_CONTENTION_LIMIT;
}
}